void fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen,
uint8_t const *key, size_t key_len)
{
- FR_MD5_CTX ctx;
- uint8_t k_ipad[65]; /* inner padding - key XORd with ipad */
- uint8_t k_opad[65]; /* outer padding - key XORd with opad */
- uint8_t tk[16];
+ fr_md5_ctx_t *ctx;
+ uint8_t k_ipad[65]; /* inner padding - key XORd with ipad */
+ uint8_t k_opad[65]; /* outer padding - key XORd with opad */
+ uint8_t tk[16];
int i;
+ ctx = fr_md5_ctx_alloc(true);
+
/* if key is longer than 64 bytes reset it to key=MD5(key) */
if (key_len > 64) {
- FR_MD5_CTX tctx;
-
- fr_md5_init(&tctx);
- fr_md5_update(&tctx, key, key_len);
- fr_md5_final(tk, &tctx);
+ fr_md5_update(ctx, key, key_len);
+ fr_md5_final(tk, ctx);
+ fr_md5_ctx_reset(ctx);
key = tk;
key_len = 16;
*/
/* start out by storing key in pads */
- memset( k_ipad, 0, sizeof(k_ipad));
- memset( k_opad, 0, sizeof(k_opad));
- memcpy( k_ipad, key, key_len);
- memcpy( k_opad, key, key_len);
+ memset(k_ipad, 0, sizeof(k_ipad));
+ memset(k_opad, 0, sizeof(k_opad));
+ memcpy(k_ipad, key, key_len);
+ memcpy(k_opad, key, key_len);
/* XOR key with ipad and opad values */
for (i = 0; i < 64; i++) {
/*
* perform inner MD5
*/
- fr_md5_init(&ctx); /* init ctx for 1st
- * pass */
- fr_md5_update(&ctx, k_ipad, 64); /* start with inner pad */
- fr_md5_update(&ctx, in, inlen); /* then in of datagram */
- fr_md5_final(digest, &ctx); /* finish up 1st pass */
+ fr_md5_update(ctx, k_ipad, 64); /* start with inner pad */
+ fr_md5_update(ctx, in, inlen); /* then in of datagram */
+ fr_md5_final(digest, ctx); /* finish up 1st pass */
+
+
/*
* perform outer MD5
*/
- fr_md5_init(&ctx); /* init ctx for 2nd
- * pass */
- fr_md5_update(&ctx, k_opad, 64); /* start with outer pad */
- fr_md5_update(&ctx, digest, 16); /* then results of 1st
- * hash */
- fr_md5_final(digest, &ctx); /* finish up 2nd pass */
+ fr_md5_ctx_reset(ctx);
+ fr_md5_update(ctx, k_opad, 64); /* start with outer pad */
+ fr_md5_update(ctx, digest, 16); /* then results of 1st hash */
+ fr_md5_final(digest, ctx); /* finish up 2nd pass */
+
+ fr_md5_ctx_free(&ctx);
}
#endif /* HAVE_OPENSSL_EVP_H */
*/
RCSID("$Id$")
+#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/util/strerror.h>
+#include <freeradius-devel/util/talloc.h>
+#include <freeradius-devel/util/thread_local.h>
+#include <talloc.h>
+
/*
* FORCE MD5 TO USE OUR MD5 HEADER FILE!
* If we don't do this, it might pick up the systems broken MD5.
*/
#include "md5.h"
-/** Calculate the MD5 hash of the contents of a buffer
+fr_thread_local_setup(fr_md5_ctx_t *, md5_ctx)
+
+/*
+ * If we have OpenSSL's EVP API available, then build wrapper functions.
*
- * @param[out] out Where to write the MD5 digest. Must be a minimum of MD5_DIGEST_LENGTH.
- * @param[in] in Data to hash.
- * @param[in] inlen Length of the data.
+ * We always need to build the local MD5 functions as OpenSSL could
+ * be operating in FIPS mode where MD5 digest functions are unavailable.
*/
-void fr_md5_calc(uint8_t out[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
+#ifdef HAVE_OPENSSL_EVP_H
+# include <openssl/evp.h>
+# include <openssl/crypto.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+# define EVP_MD_CTX_new EVP_MD_CTX_create
+# define EVP_MD_CTX_free EVP_MD_CTX_destroy
+# define EVP_MD_CTX_reset EVP_MD_CTX_cleanup
+#endif
+
+static int have_openssl_md5 = -1;
+
+static void _md5_ctx_openssl_free_on_exit(void *arg)
+{
+ EVP_MD_CTX_free(arg);
+}
+
+/** @copydoc fr_md5_ctx_reset
+ *
+ */
+static void fr_md5_openssl_ctx_reset(fr_md5_ctx_t *ctx)
{
- FR_MD5_CTX ctx;
+ EVP_MD_CTX *md_ctx = ctx;
- fr_md5_init(&ctx);
- fr_md5_update(&ctx, in, inlen);
- fr_md5_final(out, &ctx);
+ EVP_MD_CTX_reset(md_ctx);
+ EVP_DigestInit_ex(md_ctx, EVP_md5(), NULL);
}
-#ifndef HAVE_OPENSSL_EVP_H
+/** @copydoc fr_md5_ctx_copy
+ *
+ */
+static void fr_md5_openssl_ctx_copy(fr_md5_ctx_t *dst, fr_md5_ctx_t const *src)
+{
+ EVP_MD_CTX_copy_ex(dst, src);
+}
+
+/** @copydoc fr_md5_ctx_alloc
+ *
+ */
+static fr_md5_ctx_t *fr_md5_openssl_ctx_alloc(bool thread_local)
+{
+ EVP_MD_CTX *md_ctx;
+
+ /*
+ * Use the thread local ctx to avoid heap allocations.
+ */
+ if (thread_local) {
+ if (unlikely(!md5_ctx)) {
+ md_ctx = EVP_MD_CTX_new();
+ if (unlikely(!md_ctx)) {
+ oom:
+ fr_strerror_printf("Out of memory");
+ return NULL;
+ }
+ fr_thread_local_set_destructor(md5_ctx, _md5_ctx_openssl_free_on_exit, md_ctx);
+ EVP_DigestInit_ex(md_ctx, EVP_md5(), NULL);
+ } else {
+ md_ctx = md5_ctx;
+ }
+ /*
+ * If the MD5 ctx might be used across a yield point
+ * shared should be set to false, and new contexts
+ * should be allocated.
+ */
+ } else {
+ md_ctx = EVP_MD_CTX_new();
+ if (unlikely(!md_ctx)) goto oom;
+ EVP_DigestInit_ex(md_ctx, EVP_md5(), NULL);
+ }
+
+ return md_ctx;
+}
+
+/** @copydoc fr_md5_ctx_free
+ *
+ */
+static void fr_md5_openssl_ctx_free(fr_md5_ctx_t **ctx)
+{
+ if (md5_ctx && (md5_ctx == *ctx)) {
+ fr_md5_openssl_ctx_reset(*ctx);
+ *ctx = NULL;
+ return;
+ }
+
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+}
+
+/** @copydoc fr_md5_update
+ *
+ */
+static void fr_md5_openssl_update(fr_md5_ctx_t *ctx, uint8_t const *in, size_t inlen)
+{
+ EVP_DigestUpdate(ctx, in, inlen);
+}
+
+/** @copydoc fr_md5_final
+ *
+ */
+static void fr_md5_openssl_final(uint8_t out[static MD5_DIGEST_LENGTH], fr_md5_ctx_t *ctx)
+{
+ unsigned int len;
+
+ EVP_DigestFinal(ctx, out, &len);
+
+ if (!fr_cond_assert(len == MD5_DIGEST_LENGTH)) return;
+}
+#endif
+
+# define MD5_BLOCK_LENGTH 64
+typedef struct {
+ uint32_t state[4]; //!< State.
+ uint32_t count[2]; //!< Number of bits, mod 2^64.
+ uint8_t buffer[MD5_BLOCK_LENGTH]; //!< Input buffer.
+} fr_md5_ctx_local_t;
+
+
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
-/** Initialise a new MD5 context
- *
- * Set bit count to 0 and buffer to mysterious initialization constants.
- *
- * @param[out] ctx to initialise.
- */
-void fr_md5_init(FR_MD5_CTX *ctx)
-{
- ctx->count[0] = 0;
- ctx->count[1] = 0;
- ctx->state[0] = 0x67452301;
- ctx->state[1] = 0xefcdab89;
- ctx->state[2] = 0x98badcfe;
- ctx->state[3] = 0x10325476;
-}
-
-/** Feed additional data into the MD5 hashing function
- *
- * @param[in,out] ctx to update.
- * @param[in] in Data to hash.
- * @param[in] inlen Length of the data.
- */
-void fr_md5_update(FR_MD5_CTX *ctx, uint8_t const *in, size_t inlen)
-{
- size_t have, need;
-
- /* Check how many bytes we already have and how many more we need. */
- have = (size_t)((ctx->count[0] >> 3) & (MD5_BLOCK_LENGTH - 1));
- need = MD5_BLOCK_LENGTH - have;
-
- /* Update bitcount */
-/* ctx->count += (uint64_t)inlen << 3;*/
- if ((ctx->count[0] += ((uint32_t)inlen << 3)) < (uint32_t)inlen) {
- /* Overflowed ctx->count[0] */
- ctx->count[1]++;
- }
- ctx->count[1] += ((uint32_t)inlen >> 29);
-
- if (inlen >= need) {
- if (have != 0) {
- memcpy(ctx->buffer + have, in, need);
- fr_md5_transform(ctx->state, ctx->buffer);
- in += need;
- inlen -= need;
- have = 0;
- }
-
- /* Process data in MD5_BLOCK_LENGTH-byte chunks. */
- while (inlen >= MD5_BLOCK_LENGTH) {
- fr_md5_transform(ctx->state, in);
- in += MD5_BLOCK_LENGTH;
- inlen -= MD5_BLOCK_LENGTH;
- }
- }
-
- /* Handle any remaining bytes of data. */
- if (inlen != 0) memcpy(ctx->buffer + have, in, inlen);
-}
-
-/** Finalise the MD5 context and write out the hash
- *
- * Final wrapup - pad to 64-byte boundary with the bit pattern 1 0*
- * (64-bit count of bits processed, MSB-first).
- *
- * @param[out] out Where to write the MD5 digest. Minimum length of MD5_DIGEST_LENGTH.
- * @param[in,out] ctx to finalise.
- */
-void fr_md5_final(uint8_t out[static MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx)
-{
- uint8_t count[8];
- size_t padlen;
- int i;
-
- /* Convert count to 8 bytes in little endian order. */
- PUT_64BIT_LE(count, ctx->count);
-
- /* Pad out to 56 mod 64. */
- padlen = MD5_BLOCK_LENGTH -
- ((ctx->count[0] >> 3) & (MD5_BLOCK_LENGTH - 1));
- if (padlen < 1 + 8)
- padlen += MD5_BLOCK_LENGTH;
- fr_md5_update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */
- fr_md5_update(ctx, count, 8);
-
- if (out != NULL) {
- for (i = 0; i < 4; i++)
- PUT_32BIT_LE(out + i * 4, ctx->state[i]);
- }
- memset(ctx, 0, sizeof(*ctx)); /* in case it's sensitive */
-}
/* The four core functions - F1 is optimized somewhat */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
* @param[in] state 16 bytes of data to feed into the hashing function.
* @param[in,out] block MD5 digest block to update.
*/
-void fr_md5_transform(uint32_t state[static 4], uint8_t const block[static MD5_BLOCK_LENGTH])
+static void fr_md5_local_transform(uint32_t state[static 4], uint8_t const block[static MD5_BLOCK_LENGTH])
{
uint32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4];
state[2] += c;
state[3] += d;
}
+
+/** @copydoc fr_md5_ctx_reset
+ *
+ */
+static void fr_md5_local_ctx_reset(fr_md5_ctx_t *ctx)
+{
+ fr_md5_ctx_local_t *ctx_local = talloc_get_type_abort(ctx, fr_md5_ctx_local_t);
+
+ ctx_local->count[0] = 0;
+ ctx_local->count[1] = 0;
+ ctx_local->state[0] = 0x67452301;
+ ctx_local->state[1] = 0xefcdab89;
+ ctx_local->state[2] = 0x98badcfe;
+ ctx_local->state[3] = 0x10325476;
+}
+
+/** @copydoc fr_md5_ctx_copy
+ *
+ */
+static void fr_md5_local_ctx_copy(fr_md5_ctx_t *dst, fr_md5_ctx_t const *src)
+{
+ fr_md5_ctx_local_t const *ctx_local_src = talloc_get_type_abort_const(src, fr_md5_ctx_local_t);
+ fr_md5_ctx_local_t *ctx_local_dst = talloc_get_type_abort(dst, fr_md5_ctx_local_t);
+
+ memcpy(ctx_local_dst, ctx_local_src, sizeof(*ctx_local_dst));
+}
+
+static void _md5_ctx_local_free_on_exit(void *arg)
+{
+ talloc_free(arg);
+}
+
+/** @copydoc fr_md5_ctx_alloc
+ *
+ */
+static fr_md5_ctx_t *fr_md5_local_ctx_alloc(bool thread_local)
+{
+ fr_md5_ctx_local_t *ctx_local;
+
+#ifdef HAVE_OPENSSL_EVP_H
+ if (unlikely(have_openssl_md5 == -1)) {
+ /*
+ * If we're not in FIPS mode, then swap out the
+ * md5 functions, and call the OpenSSL init
+ * function.
+ */
+ if (FIPS_mode() == 0) {
+ have_openssl_md5 = 1;
+
+ /*
+ * Swap out the functions pointers
+ * for the OpenSSL versions.
+ */
+ fr_md5_ctx_reset = fr_md5_openssl_ctx_reset;
+ fr_md5_ctx_copy = fr_md5_openssl_ctx_copy;
+ fr_md5_ctx_alloc = fr_md5_openssl_ctx_alloc;
+ fr_md5_ctx_free = fr_md5_openssl_ctx_free;
+ fr_md5_update = fr_md5_openssl_update;
+ fr_md5_final = fr_md5_openssl_final;
+
+ return fr_md5_ctx_alloc(thread_local);
+ }
+
+ have_openssl_md5 = 0;
+ }
#endif
+
+ /*
+ * Use the thread local ctx to avoid heap allocations.
+ */
+ if (thread_local) {
+ if (unlikely(!md5_ctx)) {
+ ctx_local = talloc(NULL, fr_md5_ctx_local_t);
+ if (unlikely(!ctx_local)) return NULL;
+ fr_md5_local_ctx_reset(ctx_local);
+ fr_thread_local_set_destructor(md5_ctx, _md5_ctx_local_free_on_exit, ctx_local);
+ } else {
+ ctx_local = md5_ctx;
+ }
+ /*
+ * If the MD5 ctx might be used across a yield point
+ * shared should be set to false, and new contexts
+ * should be allocated.
+ */
+ } else {
+ ctx_local = talloc(NULL, fr_md5_ctx_local_t);
+ if (unlikely(!ctx_local)) return NULL;
+ fr_md5_local_ctx_reset(ctx_local);
+ }
+
+ return ctx_local;
+}
+
+/** @copydoc fr_md5_ctx_free
+ *
+ */
+static void fr_md5_local_ctx_free(fr_md5_ctx_t **ctx)
+{
+ if (md5_ctx && (md5_ctx == *ctx)) {
+ fr_md5_local_ctx_reset(*ctx);
+ *ctx = NULL;
+ return; /* Don't free the thread_local ctx */
+ }
+
+ talloc_free(*ctx);
+ *ctx = NULL;
+}
+
+/** @copydoc fr_md5_update
+ *
+ */
+static void fr_md5_local_update(fr_md5_ctx_t *ctx, uint8_t const *in, size_t inlen)
+{
+ fr_md5_ctx_local_t *ctx_local = talloc_get_type_abort(ctx, fr_md5_ctx_local_t);
+
+ size_t have, need;
+
+ /* Check how many bytes we already have and how many more we need. */
+ have = (size_t)((ctx_local->count[0] >> 3) & (MD5_BLOCK_LENGTH - 1));
+ need = MD5_BLOCK_LENGTH - have;
+
+ /* Update bitcount */
+/* ctx_local->count += (uint64_t)inlen << 3;*/
+ if ((ctx_local->count[0] += ((uint32_t)inlen << 3)) < (uint32_t)inlen) {
+ /* Overflowed ctx_local->count[0] */
+ ctx_local->count[1]++;
+ }
+ ctx_local->count[1] += ((uint32_t)inlen >> 29);
+
+ if (inlen >= need) {
+ if (have != 0) {
+ memcpy(ctx_local->buffer + have, in, need);
+ fr_md5_local_transform(ctx_local->state, ctx_local->buffer);
+ in += need;
+ inlen -= need;
+ have = 0;
+ }
+
+ /* Process data in MD5_BLOCK_LENGTH-byte chunks. */
+ while (inlen >= MD5_BLOCK_LENGTH) {
+ fr_md5_local_transform(ctx_local->state, in);
+ in += MD5_BLOCK_LENGTH;
+ inlen -= MD5_BLOCK_LENGTH;
+ }
+ }
+
+ /* Handle any remaining bytes of data. */
+ if (inlen != 0) memcpy(ctx_local->buffer + have, in, inlen);
+}
+
+/** @copydoc fr_md5_final
+ *
+ */
+static void fr_md5_local_final(uint8_t out[static MD5_DIGEST_LENGTH], fr_md5_ctx_t *ctx)
+{
+ fr_md5_ctx_local_t *ctx_local = talloc_get_type_abort(ctx, fr_md5_ctx_local_t);
+ uint8_t count[8];
+ size_t padlen;
+ int i;
+
+ /* Convert count to 8 bytes in little endian order. */
+ PUT_64BIT_LE(count, ctx_local->count);
+
+ /* Pad out to 56 mod 64. */
+ padlen = MD5_BLOCK_LENGTH -
+ ((ctx_local->count[0] >> 3) & (MD5_BLOCK_LENGTH - 1));
+ if (padlen < 1 + 8)
+ padlen += MD5_BLOCK_LENGTH;
+ fr_md5_update(ctx_local, PADDING, padlen - 8); /* padlen - 8 <= 64 */
+ fr_md5_update(ctx_local, count, 8);
+
+ if (out != NULL) {
+ for (i = 0; i < 4; i++)
+ PUT_32BIT_LE(out + i * 4, ctx_local->state[i]);
+ }
+ memset(ctx_local, 0, sizeof(*ctx_local)); /* in case it's sensitive */
+}
+
+/*
+ * Digest function pointers
+ */
+fr_md5_ctx_reset_t fr_md5_ctx_reset = fr_md5_local_ctx_reset;
+fr_md5_ctx_copy_t fr_md5_ctx_copy = fr_md5_local_ctx_copy;
+fr_md5_ctx_alloc_t fr_md5_ctx_alloc = fr_md5_local_ctx_alloc;
+fr_md5_ctx_free_t fr_md5_ctx_free = fr_md5_local_ctx_free;
+fr_md5_update_t fr_md5_update = fr_md5_local_update;
+fr_md5_final_t fr_md5_final = fr_md5_local_final;
+
+/** Calculate the MD5 hash of the contents of a buffer
+ *
+ * @param[out] out Where to write the MD5 digest. Must be a minimum of MD5_DIGEST_LENGTH.
+ * @param[in] in Data to hash.
+ * @param[in] inlen Length of the data.
+ */
+void fr_md5_calc(uint8_t out[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
+{
+ fr_md5_ctx_t *ctx;
+
+ ctx = fr_md5_ctx_alloc(true);
+ fr_md5_update(ctx, in, inlen);
+ fr_md5_final(out, ctx);
+ fr_md5_ctx_free(&ctx);
+}
* @note license is LGPL, but largely derived from a public domain source.
*
* @file src/lib/util/md5.h
+ * @brief Structures and declarations for md5.
*/
RCSIDH(md5_h, "$Id$")
#include <freeradius-devel/build.h>
#include <freeradius-devel/missing.h>
-#ifdef HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
+#include <inttypes.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
+#ifndef MD5_DIGEST_LENGTH
+# define MD5_DIGEST_LENGTH 16
#endif
-#ifdef HAVE_STDINT_H
-# include <stdint.h>
-#endif
+typedef void fr_md5_ctx_t;
-# include <string.h>
+/* md5.c */
-#ifdef HAVE_OPENSSL_EVP_H
-# include <openssl/evp.h>
-#endif
+/** Reset the ctx to allow reuse
+ *
+ * @param[in] ctx To reuse.
+ */
+typedef void (*fr_md5_ctx_reset_t)(fr_md5_ctx_t *ctx);
+extern fr_md5_ctx_reset_t fr_md5_ctx_reset;
-#ifndef MD5_DIGEST_LENGTH
-# define MD5_DIGEST_LENGTH 16
-#endif
+/** Copy the contents of a ctx
+ *
+ * @param[in] dst Where to copy the context to.
+ * @param[in] src Where to copy the context from.
+ */
+typedef void (*fr_md5_ctx_copy_t)(fr_md5_ctx_t *dst, fr_md5_ctx_t const *src);
+extern fr_md5_ctx_copy_t fr_md5_ctx_copy;
-#ifndef HAVE_OPENSSL_EVP_H
-/*
- * The MD5 code used here and in md5.c was originally retrieved from:
- * http://www.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/crypto/md5.h?rev=1.1
+/** Allocation function for MD5 digest context
*
- * This code implements the MD5 message-digest algorithm.
- * The algorithm is due to Ron Rivest. This code was
- * written by Colin Plumb in 1993, no copyright is claimed.
- * This code is in the public domain; do with it what you wish.
+ * @param[in] shared Whether we allocate a new context or use the thread local context.
+ * @return
+ * - An MD5 ctx.
+ * - NULL if out of memory.
+ */
+typedef fr_md5_ctx_t *(*fr_md5_ctx_alloc_t)(bool shared);
+extern fr_md5_ctx_alloc_t fr_md5_ctx_alloc;
+
+/** Free function for MD5 digest ctx
*
- * Equivalent code is available from RSA Data Security, Inc.
- * This code has been tested against that, and is equivalent,
- * except that you don't need to include two pages of legalese
- * with every copy.
+ * @param[in] ctx MD5 ctx to free. If the shared ctx is passed in
+ * then the ctx is reset but not freed.
*/
-# define MD5_BLOCK_LENGTH 64
-typedef struct {
- uint32_t state[4]; //!< State.
- uint32_t count[2]; //!< Number of bits, mod 2^64.
- uint8_t buffer[MD5_BLOCK_LENGTH]; //!< Input buffer.
-} FR_MD5_CTX;
+typedef void (*fr_md5_ctx_free_t)(fr_md5_ctx_t **ctx);
+extern fr_md5_ctx_free_t fr_md5_ctx_free;
-void fr_md5_init(FR_MD5_CTX *ctx);
-void fr_md5_update(FR_MD5_CTX *ctx, uint8_t const *in, size_t inlen);
-void fr_md5_final(uint8_t out[static MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx);
-void fr_md5_transform(uint32_t state[static 4], uint8_t const block[static MD5_BLOCK_LENGTH]);
-# define fr_md5_copy(_out, _in) memcpy(_out, _in, sizeof(*_out))
-#else /* HAVE_OPENSSL_EVP_H */
-USES_APPLE_DEPRECATED_API
-#include <openssl/md5.h>
-# define FR_MD5_CTX MD5_CTX
-# define fr_md5_init MD5_Init
-# define fr_md5_update MD5_Update
-# define fr_md5_final MD5_Final
-# define fr_md5_transform MD5_Transform
-# define fr_md5_copy(_out, _in) memcpy(_out, _in, sizeof(*_out))
-#endif
+/** Ingest plaintext into the digest
+ *
+ * @param[in] ctx To ingest data into.
+ * @param[in] in Data to ingest.
+ * @param[in] inlen Length of data to ingest.
+ */
+typedef void (*fr_md5_update_t)(fr_md5_ctx_t *ctx, uint8_t const *in, size_t inlen);
+extern fr_md5_update_t fr_md5_update;
-/* hmac.c */
-void fr_hmac_md5(uint8_t digest[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen,
- uint8_t const *key, size_t key_len);
-/* md5.c */
-void fr_md5_calc(uint8_t out[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen);
+/** Finalise the ctx, producing the digest
+ *
+ * @param[out] out The MD5 digest.
+ * @param[in] ctx To finalise.
+ */
+typedef void (*fr_md5_final_t)(uint8_t out[static MD5_DIGEST_LENGTH], fr_md5_ctx_t *ctx);
+extern fr_md5_final_t fr_md5_final;
+
+/** Perform a single digest operation on a single input buffer
+ *
+ */
+void fr_md5_calc(uint8_t out[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen);
+/* hmac.c */
+void fr_hmac_md5(uint8_t digest[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen,
+ uint8_t const *key, size_t key_len);
#ifdef __cplusplus
}
#endif
static void calc_apop_digest(uint8_t *buffer, uint8_t const *challenge,
size_t challen, char const *password)
{
- FR_MD5_CTX context;
+ fr_md5_ctx_t *md5_ctx;
- fr_md5_init(&context);
- fr_md5_update(&context, challenge, challen);
- fr_md5_update(&context, (uint8_t const *) password, strlen(password));
- fr_md5_final(buffer, &context);
+ md5_ctx = fr_md5_ctx_alloc(true);
+ fr_md5_update(md5_ctx, challenge, challen);
+ fr_md5_update(md5_ctx, (uint8_t const *) password, strlen(password));
+ fr_md5_final(buffer, md5_ctx);
+ fr_md5_ctx_free(&md5_ctx);
}
-
static void calc_md5_digest(uint8_t *buffer, uint8_t const *challenge, size_t challen, char const *password)
{
- uint8_t buf[1024];
- int i;
- FR_MD5_CTX context;
+ uint8_t buf[1024];
+ int i;
+ fr_md5_ctx_t *md5_ctx;
memset(buf, 0, 1024);
memset(buf, 0x36, 64);
- for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
- memcpy(buf+64, challenge, challen);
- fr_md5_init(&context);
- fr_md5_update(&context, buf, 64+challen);
+ for (i = 0; i < 64 && password[i]; i++) buf[i] ^= password[i];
+ memcpy(buf + 64, challenge, challen);
+
+ md5_ctx = fr_md5_ctx_alloc(true);
+
+ fr_md5_update(md5_ctx, buf, 64 + challen);
memset(buf, 0x5c, 64);
- for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
- fr_md5_final(buf+64,&context);
- fr_md5_init(&context);
- fr_md5_update(&context,buf,64+16);
- fr_md5_final(buffer,&context);
+ for (i = 0; i < 64 && password[i]; i++) buf[i] ^= password[i];
+ fr_md5_final(buf + 64, md5_ctx);
+
+ fr_md5_ctx_reset(md5_ctx);
+ fr_md5_update(md5_ctx, buf, 64 + 16);
+ fr_md5_final(buffer, md5_ctx);
+ fr_md5_ctx_free(&md5_ctx);
}
static void calc_md4_digest(uint8_t *buffer, uint8_t const *challenge, size_t challen, char const *password)
static rlm_rcode_t CC_HINT(nonnull) pap_auth_smd5(rlm_pap_t const *inst, REQUEST *request, VALUE_PAIR *vp)
{
- FR_MD5_CTX md5_context;
+ fr_md5_ctx_t *md5_ctx;
uint8_t digest[128];
RDEBUG("Comparing with \"known-good\" SMD5-Password");
return RLM_MODULE_INVALID;
}
- fr_md5_init(&md5_context);
- fr_md5_update(&md5_context, request->password->vp_octets, request->password->vp_length);
- fr_md5_update(&md5_context, vp->vp_octets + MD5_DIGEST_LENGTH, vp->vp_length - MD5_DIGEST_LENGTH);
- fr_md5_final(digest, &md5_context);
+ md5_ctx = fr_md5_ctx_alloc(true);
+ fr_md5_update(md5_ctx, request->password->vp_octets, request->password->vp_length);
+ fr_md5_update(md5_ctx, vp->vp_octets + MD5_DIGEST_LENGTH, vp->vp_length - MD5_DIGEST_LENGTH);
+ fr_md5_final(digest, md5_ctx);
+ fr_md5_ctx_free(&md5_ctx);
/*
* Compare only the MD5 hash results, not the salt.
*/
void fr_radius_ascend_secret(uint8_t *digest, uint8_t const *vector, char const *secret, uint8_t const *value)
{
- FR_MD5_CTX context;
- int i;
+ fr_md5_ctx_t *md5_ctx;
+ int i;
- fr_md5_init(&context);
- fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
- fr_md5_update(&context, (uint8_t const *) secret, talloc_array_length(secret) - 1);
- fr_md5_final(digest, &context);
+ md5_ctx = fr_md5_ctx_alloc(true);
+ fr_md5_update(md5_ctx, vector, AUTH_VECTOR_LEN);
+ fr_md5_update(md5_ctx, (uint8_t const *) secret, talloc_array_length(secret) - 1);
+ fr_md5_final(digest, md5_ctx);
+ fr_md5_ctx_free(&md5_ctx);
for (i = 0; i < AUTH_VECTOR_LEN; i++ ) digest[i] ^= value[i];
}
{
uint8_t *msg, *end;
size_t packet_len = (packet[2] << 8) | packet[3];
- FR_MD5_CTX context;
/*
* No real limit on secret length, this is just
/*
* Request / Response Authenticator = MD5(packet + secret)
*/
- fr_md5_init(&context);
- fr_md5_update(&context, packet, packet_len);
- fr_md5_update(&context, secret, secret_len);
- fr_md5_final(packet + 4, &context);
+ {
+ fr_md5_ctx_t *md5_ctx;
+
+ md5_ctx = fr_md5_ctx_alloc(true);
+ fr_md5_update(md5_ctx, packet, packet_len);
+ fr_md5_update(md5_ctx, secret, secret_len);
+ fr_md5_final(packet + 4, md5_ctx);
+ fr_md5_ctx_free(&md5_ctx);
+ }
return 0;
}
ssize_t fr_radius_decode_tunnel_password(uint8_t *passwd, size_t *pwlen,
char const *secret, uint8_t const *vector, bool tunnel_password_zeros)
{
- FR_MD5_CTX context, old;
+ fr_md5_ctx_t *md5_ctx, *md5_ctx_old;
uint8_t digest[AUTH_VECTOR_LEN];
int secretlen;
size_t i, n, encrypted_len, embedded_len;
*/
secretlen = talloc_array_length(secret) - 1;
- fr_md5_init(&context);
- fr_md5_update(&context, (uint8_t const *) secret, secretlen);
- fr_md5_copy(&old, &context); /* save intermediate work */
+ md5_ctx = fr_md5_ctx_alloc(false);
+ md5_ctx_old = fr_md5_ctx_alloc(true);
+
+ fr_md5_update(md5_ctx, (uint8_t const *) secret, secretlen);
+ fr_md5_ctx_copy(md5_ctx_old, md5_ctx); /* save intermediate work */
/*
* Set up the initial key:
*
* b(1) = MD5(secret + vector + salt)
*/
- fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
- fr_md5_update(&context, passwd, 2);
+ fr_md5_update(md5_ctx, vector, AUTH_VECTOR_LEN);
+ fr_md5_update(md5_ctx, passwd, 2);
embedded_len = 0;
for (n = 0; n < encrypted_len; n += AUTH_PASS_LEN) {
if (n == 0) {
base = 1;
- fr_md5_final(digest, &context);
- fr_md5_copy(&context, &old);
+ fr_md5_final(digest, md5_ctx);
+ fr_md5_ctx_copy(md5_ctx, md5_ctx_old);
/*
* A quick check: decrypt the first octet
if (embedded_len > encrypted_len) {
fr_strerror_printf("Tunnel Password is too long for the attribute "
"(shared secret is probably incorrect!)");
+ fr_md5_ctx_free(&md5_ctx);
+ fr_md5_ctx_free(&md5_ctx_old);
return -1;
}
- fr_md5_update(&context, passwd + 2, block_len);
+ fr_md5_update(md5_ctx, passwd + 2, block_len);
} else {
base = 0;
- fr_md5_final(digest, &context);
+ fr_md5_final(digest, md5_ctx);
- fr_md5_copy(&context, &old);
- fr_md5_update(&context, passwd + n + 2, block_len);
+ fr_md5_ctx_copy(md5_ctx, md5_ctx_old);
+ fr_md5_update(md5_ctx, passwd + n + 2, block_len);
}
for (i = base; i < block_len; i++) {
}
}
+ fr_md5_ctx_free(&md5_ctx);
+ fr_md5_ctx_free(&md5_ctx_old);
+
/*
* Check trailing bytes
*/
*/
ssize_t fr_radius_decode_password(char *passwd, size_t pwlen, char const *secret, uint8_t const *vector)
{
- FR_MD5_CTX context, old;
+ fr_md5_ctx_t *md5_ctx, *md5_ctx_old;
uint8_t digest[AUTH_VECTOR_LEN];
int i;
size_t n, secretlen;
*/
secretlen = talloc_array_length(secret) - 1;
- fr_md5_init(&context);
- fr_md5_update(&context, (uint8_t const *) secret, secretlen);
- fr_md5_copy(&old, &context); /* save intermediate work */
+ md5_ctx = fr_md5_ctx_alloc(false);
+ md5_ctx_old = fr_md5_ctx_alloc(true);
+
+ fr_md5_update(md5_ctx, (uint8_t const *) secret, secretlen);
+ fr_md5_ctx_copy(md5_ctx_old, md5_ctx); /* save intermediate work */
/*
* The inverse of the code above.
*/
for (n = 0; n < pwlen; n += AUTH_PASS_LEN) {
if (n == 0) {
- fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
- fr_md5_final(digest, &context);
+ fr_md5_update(md5_ctx, vector, AUTH_VECTOR_LEN);
+ fr_md5_final(digest, md5_ctx);
- fr_md5_copy(&context, &old);
+ fr_md5_ctx_copy(md5_ctx, md5_ctx_old);
if (pwlen > AUTH_PASS_LEN) {
- fr_md5_update(&context, (uint8_t *) passwd, AUTH_PASS_LEN);
+ fr_md5_update(md5_ctx, (uint8_t *) passwd, AUTH_PASS_LEN);
}
} else {
- fr_md5_final(digest, &context);
+ fr_md5_final(digest, md5_ctx);
- fr_md5_copy(&context, &old);
+ fr_md5_ctx_copy(md5_ctx, md5_ctx_old);
if (pwlen > (n + AUTH_PASS_LEN)) {
- fr_md5_update(&context, (uint8_t *) passwd + n, AUTH_PASS_LEN);
+ fr_md5_update(md5_ctx, (uint8_t *) passwd + n, AUTH_PASS_LEN);
}
}
for (i = 0; i < AUTH_PASS_LEN; i++) passwd[i + n] ^= digest[i];
}
+ fr_md5_ctx_free(&md5_ctx);
+ fr_md5_ctx_free(&md5_ctx_old);
+
done:
passwd[pwlen] = '\0';
return strlen(passwd);
*/
int fr_radius_encode_password(char *passwd, size_t *pwlen, char const *secret, uint8_t const *vector)
{
- FR_MD5_CTX context, old;
+ fr_md5_ctx_t *md5_ctx, *md5_ctx_old;
uint8_t digest[AUTH_VECTOR_LEN];
int i, n, secretlen;
int len;
*/
secretlen = talloc_array_length(secret) - 1;
- fr_md5_init(&context);
- fr_md5_update(&context, (uint8_t const *) secret, secretlen);
- fr_md5_copy(&old, &context); /* save intermediate work */
+ md5_ctx = fr_md5_ctx_alloc(false);
+ md5_ctx_old = fr_md5_ctx_alloc(true);
+
+ fr_md5_update(md5_ctx, (uint8_t const *) secret, secretlen);
+ fr_md5_ctx_copy(md5_ctx_old, md5_ctx); /* save intermediate work */
/*
* Encrypt it in place. Don't bother checking
*/
for (n = 0; n < len; n += AUTH_PASS_LEN) {
if (n == 0) {
- fr_md5_update(&context, vector, AUTH_PASS_LEN);
- fr_md5_final(digest, &context);
+ fr_md5_update(md5_ctx, vector, AUTH_PASS_LEN);
+ fr_md5_final(digest, md5_ctx);
} else {
- fr_md5_copy(&context, &old);
- fr_md5_update(&context, (uint8_t *) passwd + n - AUTH_PASS_LEN, AUTH_PASS_LEN);
- fr_md5_final(digest, &context);
+ fr_md5_ctx_copy(md5_ctx, md5_ctx_old);
+ fr_md5_update(md5_ctx, (uint8_t *) passwd + n - AUTH_PASS_LEN, AUTH_PASS_LEN);
+ fr_md5_final(digest, md5_ctx);
}
for (i = 0; i < AUTH_PASS_LEN; i++) passwd[i + n] ^= digest[i];
}
+ fr_md5_ctx_free(&md5_ctx);
+ fr_md5_ctx_free(&md5_ctx_old);
+
return 0;
}
static void encode_password(uint8_t *out, ssize_t *outlen, uint8_t const *input, size_t inlen,
char const *secret, uint8_t const *vector)
{
- FR_MD5_CTX context, old;
+ fr_md5_ctx_t *md5_ctx, *md5_ctx_old;
uint8_t digest[AUTH_VECTOR_LEN];
uint8_t passwd[MAX_PASS_LEN];
size_t i, n;
}
*outlen = len;
- fr_md5_init(&context);
- fr_md5_update(&context, (uint8_t const *) secret, talloc_array_length(secret) - 1);
- fr_md5_copy(&old, &context);
+ md5_ctx = fr_md5_ctx_alloc(false);
+ md5_ctx_old = fr_md5_ctx_alloc(true);
+
+ fr_md5_update(md5_ctx, (uint8_t const *) secret, talloc_array_length(secret) - 1);
+ fr_md5_ctx_copy(md5_ctx_old, md5_ctx);
/*
* Do first pass.
*/
- fr_md5_update(&context, vector, AUTH_PASS_LEN);
+ fr_md5_update(md5_ctx, vector, AUTH_PASS_LEN);
for (n = 0; n < len; n += AUTH_PASS_LEN) {
if (n > 0) {
- fr_md5_copy(&context, &old);
- fr_md5_update(&context, passwd + n - AUTH_PASS_LEN, AUTH_PASS_LEN);
+ fr_md5_ctx_copy(md5_ctx, md5_ctx_old);
+ fr_md5_update(md5_ctx, passwd + n - AUTH_PASS_LEN, AUTH_PASS_LEN);
}
- fr_md5_final(digest, &context);
+ fr_md5_final(digest, md5_ctx);
for (i = 0; i < AUTH_PASS_LEN; i++) passwd[i + n] ^= digest[i];
}
+ fr_md5_ctx_free(&md5_ctx);
+ fr_md5_ctx_free(&md5_ctx_old);
+
memcpy(out, passwd, len);
}
uint8_t const *input, size_t inlen, size_t freespace,
char const *secret, uint8_t const *vector)
{
- FR_MD5_CTX context, old;
+ fr_md5_ctx_t *md5_ctx, *md5_ctx_old;
uint8_t digest[AUTH_VECTOR_LEN];
size_t i, n;
size_t encrypted_len;
out[1] = fr_rand();
out[2] = inlen; /* length of the password string */
- fr_md5_init(&context);
- fr_md5_update(&context, (uint8_t const *) secret, talloc_array_length(secret) - 1);
- fr_md5_copy(&old, &context);
+ md5_ctx = fr_md5_ctx_alloc(false);
+ md5_ctx_old = fr_md5_ctx_alloc(true);
+
+ fr_md5_update(md5_ctx, (uint8_t const *) secret, talloc_array_length(secret) - 1);
+ fr_md5_ctx_copy(md5_ctx_old, md5_ctx);
- fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
- fr_md5_update(&context, &out[0], 2);
+ fr_md5_update(md5_ctx, vector, AUTH_VECTOR_LEN);
+ fr_md5_update(md5_ctx, &out[0], 2);
for (n = 0; n < encrypted_len; n += AUTH_PASS_LEN) {
size_t block_len;
if (n > 0) {
- fr_md5_copy(&context, &old);
- fr_md5_update(&context, out + 2 + n - AUTH_PASS_LEN, AUTH_PASS_LEN);
+ fr_md5_ctx_copy(md5_ctx, md5_ctx_old);
+ fr_md5_update(md5_ctx, out + 2 + n - AUTH_PASS_LEN, AUTH_PASS_LEN);
}
- fr_md5_final(digest, &context);
+ fr_md5_final(digest, md5_ctx);
if ((2 + n + AUTH_PASS_LEN) < freespace) {
block_len = AUTH_PASS_LEN;
for (i = 0; i < block_len; i++) out[i + 2 + n] ^= digest[i];
}
+
+ fr_md5_ctx_free(&md5_ctx);
+ fr_md5_ctx_free(&md5_ctx_old);
}
static ssize_t encode_tlv_hdr_internal(uint8_t *out, size_t outlen,
static ssize_t test_encode(UNUSED void const *instance, REQUEST *request, uint8_t *buffer, size_t buffer_len)
{
- FR_MD5_CTX context;
+ fr_md5_ctx_t *md5_ctx;
fr_radius_packet_ctx_t const *pc = talloc_get_type_abort_const(request->async->listen->app_instance,
fr_radius_packet_ctx_t);
memcpy(buffer + 4, pc->vector, 16);
- fr_md5_init(&context);
- fr_md5_update(&context, buffer, 20);
- fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
- fr_md5_final(buffer + 4, &context);
+ md5_ctx = fr_md5_ctx_alloc(true);
+ fr_md5_update(md5_ctx, buffer, 20);
+ fr_md5_update(md5_ctx, (uint8_t const *) secret, strlen(secret));
+ fr_md5_final(buffer + 4, md5_ctx);
+ fr_md5_ctx_free(&md5_ctx);
return 20;
}
static ssize_t test_encode(void const *instance, REQUEST *request, uint8_t *buffer, size_t buffer_len)
{
- FR_MD5_CTX context;
+ fr_md5_ctx_t *md5_ctx;
fr_listen_test_t const *pc = instance;
MPRINT1("\t\tENCODE >>> request %"PRIu64"- data %p %p room %zd\n", request->number, pc, buffer, buffer_len);
memcpy(buffer + 4, tpc.vector, 16);
- fr_md5_init(&context);
- fr_md5_update(&context, buffer, 20);
- fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
- fr_md5_final(buffer + 4, &context);
+ md5_ctx = fr_md5_ctx_alloc(true);
+ fr_md5_update(md5_ctx, buffer, 20);
+ fr_md5_update(md5_ctx, (uint8_t const *) secret, strlen(secret));
+ fr_md5_final(buffer + 4, md5_ctx);
+ fr_md5_ctx_free(&md5_ctx);
return 20;
}