return _xlat_change_case(true, ctx, out, request, in);
}
+
+/** Calculate the MD4 hash of a string or attribute.
+ *
+ * Example: "%{md4:foo}" == "0ac6700c491d70fb8650940b1ca1e4b2"
+ */
+static xlat_action_t md4_xlat(TALLOC_CTX *ctx, fr_cursor_t *out,
+ REQUEST *request, UNUSED void const *xlat_inst, UNUSED void *xlat_thread_inst,
+ fr_value_box_t **in)
+{
+ uint8_t digest[MD5_DIGEST_LENGTH];
+ fr_md4_ctx_t *md4_ctx;
+ fr_value_box_t *vb;
+
+ /*
+ * Concatenate all input if there is some
+ */
+ if (*in && fr_value_box_list_concat(ctx, *in, in, FR_TYPE_OCTETS, true) < 0) {
+ RPEDEBUG("Failed concatenating input");
+ return XLAT_ACTION_FAIL;
+ }
+
+ md4_ctx = fr_md4_ctx_alloc(true);
+ if (*in) {
+ fr_md4_update(md4_ctx, (*in)->vb_octets, (*in)->vb_length);
+ } else {
+ /* MD4 of empty string */
+ fr_md4_update(md4_ctx, NULL, 0);
+ }
+ fr_md4_final(digest, md4_ctx);
+ fr_md4_ctx_free(&md4_ctx);
+
+ MEM(vb = fr_value_box_alloc_null(ctx));
+ fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
+
+ fr_cursor_append(out, vb);
+
+ return XLAT_ACTION_DONE;
+}
+
/** Calculate the MD5 hash of a string or attribute.
*
* Example: "%{md5:foo}" == "acbd18db4cc2f85cedef654fccc4a4d8"
xlat_async_register(NULL, "hex", hex_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
xlat_async_register(NULL, "hmacmd5", hmac_md5_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
xlat_async_register(NULL, "hmacsha1", hmac_sha1_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ xlat_async_register(NULL, "md4", md4_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
xlat_async_register(NULL, "md5", md5_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
xlat_async_register(NULL, "rand", rand_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
xlat_async_register(NULL, "randstr", randstr_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
*/
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>
+
/*
* FORCE MD4 TO USE OUR MD4 HEADER FILE!
* If we don't do this, it might pick up the systems broken MD4.
*/
-#include <freeradius-devel/util/md4.h>
+#include "md4.h"
-/** Calculate the MD4 hash of the contents of a buffer
+fr_thread_local_setup(fr_md4_ctx_t *, md4_ctx)
+
+/*
+ * If we have OpenSSL's EVP API available, then build wrapper functions.
*
- * @param[out] out Where to write the MD4 digest. Must be a minimum of MD4_DIGEST_LENGTH.
- * @param[in] in Data to hash.
- * @param[in] inlen Length of the data.
+ * We always need to build the local MD4 functions as OpenSSL could
+ * be operating in FIPS mode where MD4 digest functions are unavailable.
*/
-void fr_md4_calc(uint8_t out[static MD4_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_md4 = -1;
+
+static void _md4_ctx_openssl_free_on_exit(void *arg)
{
- FR_MD4_CTX ctx;
+ EVP_MD_CTX_free(arg);
+}
- fr_md4_init(&ctx);
- fr_md4_update(&ctx, in, inlen);
- fr_md4_final(out, &ctx);
+/** @copydoc fr_md4_ctx_reset
+ *
+ */
+static void fr_md4_openssl_ctx_reset(fr_md4_ctx_t *ctx)
+{
+ EVP_MD_CTX *md_ctx = ctx;
+
+ EVP_MD_CTX_reset(md_ctx);
+ EVP_DigestInit_ex(md_ctx, EVP_md4(), NULL);
+}
+
+/** @copydoc fr_md4_ctx_copy
+ *
+ */
+static void fr_md4_openssl_ctx_copy(fr_md4_ctx_t *dst, fr_md4_ctx_t const *src)
+{
+ EVP_MD_CTX_copy_ex(dst, src);
}
-#ifndef HAVE_OPENSSL_EVP_H
+/** @copydoc fr_md4_ctx_alloc
+ *
+ */
+static fr_md4_ctx_t *fr_md4_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(!md4_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(md4_ctx, _md4_ctx_openssl_free_on_exit, md_ctx);
+ EVP_DigestInit_ex(md_ctx, EVP_md4(), NULL);
+ } else {
+ md_ctx = md4_ctx;
+ }
+ /*
+ * If the MD4 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_md4(), NULL);
+ }
+
+ return md_ctx;
+}
+
+/** @copydoc fr_md4_ctx_free
+ *
+ */
+static void fr_md4_openssl_ctx_free(fr_md4_ctx_t **ctx)
+{
+ if (md4_ctx && (md4_ctx == *ctx)) {
+ fr_md4_openssl_ctx_reset(*ctx);
+ *ctx = NULL;
+ return;
+ }
+
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+}
+
+/** @copydoc fr_md4_update
+ *
+ */
+static void fr_md4_openssl_update(fr_md4_ctx_t *ctx, uint8_t const *in, size_t inlen)
+{
+ EVP_DigestUpdate(ctx, in, inlen);
+}
+
+/** @copydoc fr_md4_final
+ *
+ */
+static void fr_md4_openssl_final(uint8_t out[static MD4_DIGEST_LENGTH], fr_md4_ctx_t *ctx)
+{
+ unsigned int len;
+
+ EVP_DigestFinal(ctx, out, &len);
+
+ if (!fr_cond_assert(len == MD4_DIGEST_LENGTH)) return;
+}
+#endif
+
/*
* This code implements the MD4 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* needed on buffers full of bytes, and then call fr_md4_final, which
* will fill a supplied 16-byte array with the digest.
*/
-
#ifndef WORDS_BIGENDIAN
# define htole32_4(buf) /* Nothing */
# define htole32_14(buf) /* Nothing */
} while (0)
#endif
-/** Initialise a new MD4 context
- *
- * Set bit count to 0 and buffer to mysterious initialization constants.
- *
- * @param[out] ctx to initialise.
- */
-void fr_md4_init(FR_MD4_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 MD4 hashing function
- *
- * @param[in,out] ctx to update.
- * @param[in] in Data to hash.
- * @param[in] inlen Length of the data.
- */
-void fr_md4_update(FR_MD4_CTX *ctx, uint8_t const *in, size_t inlen)
-{
- uint32_t count;
-
- /* Bytes already stored in ctx->buffer */
- count = (uint32_t)((ctx->count[0] >> 3) & 0x3f);
-
- /* 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);
-
- /* Handle any leading odd-sized chunks */
- if (count) {
- unsigned char *p = (unsigned char *)ctx->buffer + count;
-
- count = MD4_BLOCK_LENGTH - count;
- if (inlen < count) {
- memcpy(p, in, inlen);
- return;
- }
- memcpy(p, in, count);
- htole32_16((uint32_t *)ctx->buffer);
- fr_md4_transform(ctx->state, ctx->buffer);
- in += count;
- inlen -= count;
- }
-
- /* Process data in MD4_BLOCK_LENGTH-byte chunks */
- while (inlen >= MD4_BLOCK_LENGTH) {
- memcpy(ctx->buffer, in, MD4_BLOCK_LENGTH);
- htole32_16((uint32_t *)ctx->buffer);
- fr_md4_transform(ctx->state, ctx->buffer);
- in += MD4_BLOCK_LENGTH;
- inlen -= MD4_BLOCK_LENGTH;
- }
-
- /* Handle any remaining bytes of data. */
- memcpy(ctx->buffer, in, inlen);
-}
-
-/** Finalise the MD4 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 MD4 digest. Minimum length of MD4_DIGEST_LENGTH.
- * @param[in,out] ctx to finalise.
- */
-void fr_md4_final(uint8_t out[static MD4_DIGEST_LENGTH], FR_MD4_CTX *ctx)
-{
- uint32_t count;
- unsigned char *p;
-
- /* number of bytes mod 64 */
- count = (uint32_t)(ctx->count[0] >> 3) & 0x3f;
-
- /*
- * Set the first char of padding to 0x80.
- * This is safe since there is always at least one byte free.
- */
- p = ctx->buffer + count;
- *p++ = 0x80;
-
- /* Bytes of padding needed to make 64 bytes */
- count = 64 - 1 - count;
-
- /* Pad out to 56 mod 64 */
- if (count < 8) {
- /* Two lots of padding: Pad the first block to 64 bytes */
- memset(p, 0, count);
- htole32_16((uint32_t *)ctx->buffer);
- fr_md4_transform(ctx->state, ctx->buffer);
-
- /* Now fill the next block with 56 bytes */
- memset(ctx->buffer, 0, 56);
- } else {
- /* Pad block to 56 bytes */
- memset(p, 0, count - 8);
- }
- htole32_14((uint32_t *)ctx->buffer);
-
- /* Append bit count and transform */
- ((uint32_t *)ctx->buffer)[14] = ctx->count[0];
- ((uint32_t *)ctx->buffer)[15] = ctx->count[1];
-
- fr_md4_transform(ctx->state, ctx->buffer);
- htole32_4(ctx->state);
- memcpy(out, ctx->state, MD4_DIGEST_LENGTH);
- memset(ctx, 0, sizeof(*ctx)); /* in case it's sensitive */
-}
+#define MD4_BLOCK_LENGTH 64
/* The three 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 MD4 digest block to update.
*/
-void fr_md4_transform(uint32_t state[static 4], uint8_t const block[static MD4_BLOCK_LENGTH])
+static void fr_md4_local_transform(uint32_t state[static 4], uint8_t const block[static MD4_BLOCK_LENGTH])
{
uint32_t a, b, c, d;
uint32_t const *in = (uint32_t const *)block;
state[2] += c;
state[3] += d;
}
+
+typedef struct {
+ uint32_t state[4]; //!< State.
+ uint32_t count[2]; //!< Number of bits, mod 2^64.
+ uint8_t buffer[MD4_BLOCK_LENGTH]; //!< Input buffer.
+} fr_md4_ctx_local_t;
+
+/** @copydoc fr_md4_ctx_reset
+ *
+ */
+static void fr_md4_local_ctx_reset(fr_md4_ctx_t *ctx)
+{
+ fr_md4_ctx_local_t *ctx_local = talloc_get_type_abort(ctx, fr_md4_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_md4_ctx_copy
+ *
+ */
+static void fr_md4_local_ctx_copy(fr_md4_ctx_t *dst, fr_md4_ctx_t const *src)
+{
+ fr_md4_ctx_local_t const *ctx_local_src = talloc_get_type_abort_const(src, fr_md4_ctx_local_t);
+ fr_md4_ctx_local_t *ctx_local_dst = talloc_get_type_abort(dst, fr_md4_ctx_local_t);
+
+ memcpy(ctx_local_dst, ctx_local_src, sizeof(*ctx_local_dst));
+}
+
+static void _md4_ctx_local_free_on_exit(void *arg)
+{
+ talloc_free(arg);
+}
+
+/** @copydoc fr_md4_ctx_alloc
+ *
+ */
+static fr_md4_ctx_t *fr_md4_local_ctx_alloc(bool thread_local)
+{
+ fr_md4_ctx_local_t *ctx_local;
+
+#ifdef HAVE_OPENSSL_EVP_H
+ if (unlikely(have_openssl_md4 == -1)) {
+ /*
+ * If we're not in FIPS mode, then swap out the
+ * md4 functions, and call the OpenSSL init
+ * function.
+ */
+ if (FIPS_mode() == 0) {
+ have_openssl_md4 = 1;
+
+ /*
+ * Swap out the functions pointers
+ * for the OpenSSL versions.
+ */
+ fr_md4_ctx_reset = fr_md4_openssl_ctx_reset;
+ fr_md4_ctx_copy = fr_md4_openssl_ctx_copy;
+ fr_md4_ctx_alloc = fr_md4_openssl_ctx_alloc;
+ fr_md4_ctx_free = fr_md4_openssl_ctx_free;
+ fr_md4_update = fr_md4_openssl_update;
+ fr_md4_final = fr_md4_openssl_final;
+
+ return fr_md4_ctx_alloc(thread_local);
+ }
+
+ have_openssl_md4 = 0;
+ }
#endif
+
+ /*
+ * Use the thread local ctx to avoid heap allocations.
+ */
+ if (thread_local) {
+ if (unlikely(!md4_ctx)) {
+ ctx_local = talloc(NULL, fr_md4_ctx_local_t);
+ if (unlikely(!ctx_local)) return NULL;
+ fr_md4_local_ctx_reset(ctx_local);
+ fr_thread_local_set_destructor(md4_ctx, _md4_ctx_local_free_on_exit, ctx_local);
+ } else {
+ ctx_local = md4_ctx;
+ }
+ /*
+ * If the MD4 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_md4_ctx_local_t);
+ if (unlikely(!ctx_local)) return NULL;
+ fr_md4_local_ctx_reset(ctx_local);
+ }
+
+ return ctx_local;
+}
+
+/** @copydoc fr_md4_ctx_free
+ *
+ */
+static void fr_md4_local_ctx_free(fr_md4_ctx_t **ctx)
+{
+ if (md4_ctx && (md4_ctx == *ctx)) {
+ fr_md4_local_ctx_reset(*ctx);
+ *ctx = NULL;
+ return; /* Don't free the thread_local ctx */
+ }
+
+ talloc_free(*ctx);
+ *ctx = NULL;
+}
+
+/** @copydoc fr_md4_update
+ *
+ */
+static void fr_md4_local_update(fr_md4_ctx_t *ctx, uint8_t const *in, size_t inlen)
+{
+ uint32_t count;
+ fr_md4_ctx_local_t *ctx_local = talloc_get_type_abort(ctx, fr_md4_ctx_local_t);
+
+ /* Bytes already stored in ctx_local->buffer */
+ count = (uint32_t)((ctx_local->count[0] >> 3) & 0x3f);
+
+ /* 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);
+
+ /* Handle any leading odd-sized chunks */
+ if (count) {
+ unsigned char *p = (unsigned char *)ctx_local->buffer + count;
+
+ count = MD4_BLOCK_LENGTH - count;
+ if (inlen < count) {
+ memcpy(p, in, inlen);
+ return;
+ }
+ memcpy(p, in, count);
+ htole32_16((uint32_t *)ctx_local->buffer);
+ fr_md4_local_transform(ctx_local->state, ctx_local->buffer);
+ in += count;
+ inlen -= count;
+ }
+
+ /* Process data in MD4_BLOCK_LENGTH-byte chunks */
+ while (inlen >= MD4_BLOCK_LENGTH) {
+ memcpy(ctx_local->buffer, in, MD4_BLOCK_LENGTH);
+ htole32_16((uint32_t *)ctx_local->buffer);
+ fr_md4_local_transform(ctx_local->state, ctx_local->buffer);
+ in += MD4_BLOCK_LENGTH;
+ inlen -= MD4_BLOCK_LENGTH;
+ }
+
+ /* Handle any remaining bytes of data. */
+ memcpy(ctx_local->buffer, in, inlen);
+}
+
+/** @copydoc fr_md4_final
+ *
+ */
+static void fr_md4_local_final(uint8_t out[static MD4_DIGEST_LENGTH], fr_md4_ctx_t *ctx)
+{
+ uint32_t count;
+ unsigned char *p;
+ fr_md4_ctx_local_t *ctx_local = talloc_get_type_abort(ctx, fr_md4_ctx_local_t);
+
+ /* number of bytes mod 64 */
+ count = (uint32_t)(ctx_local->count[0] >> 3) & 0x3f;
+
+ /*
+ * Set the first char of padding to 0x80.
+ * This is safe since there is always at least one byte free.
+ */
+ p = ctx_local->buffer + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ htole32_16((uint32_t *)ctx_local->buffer);
+ fr_md4_local_transform(ctx_local->state, ctx_local->buffer);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx_local->buffer, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ htole32_14((uint32_t *)ctx_local->buffer);
+
+ /* Append bit count and transform */
+ ((uint32_t *)ctx_local->buffer)[14] = ctx_local->count[0];
+ ((uint32_t *)ctx_local->buffer)[15] = ctx_local->count[1];
+
+ fr_md4_local_transform(ctx_local->state, ctx_local->buffer);
+ htole32_4(ctx_local->state);
+ memcpy(out, ctx_local->state, MD4_DIGEST_LENGTH);
+ memset(ctx_local, 0, sizeof(*ctx_local)); /* in case it's sensitive */
+}
+
+/*
+ * Digest function pointers
+ */
+fr_md4_ctx_reset_t fr_md4_ctx_reset = fr_md4_local_ctx_reset;
+fr_md4_ctx_copy_t fr_md4_ctx_copy = fr_md4_local_ctx_copy;
+fr_md4_ctx_alloc_t fr_md4_ctx_alloc = fr_md4_local_ctx_alloc;
+fr_md4_ctx_free_t fr_md4_ctx_free = fr_md4_local_ctx_free;
+fr_md4_update_t fr_md4_update = fr_md4_local_update;
+fr_md4_final_t fr_md4_final = fr_md4_local_final;
+
+/** Calculate the MD4 hash of the contents of a buffer
+ *
+ * @param[out] out Where to write the MD4 digest. Must be a minimum of MD4_DIGEST_LENGTH.
+ * @param[in] in Data to hash.
+ * @param[in] inlen Length of the data.
+ */
+void fr_md4_calc(uint8_t out[static MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
+{
+ fr_md4_ctx_t *ctx;
+
+ ctx = fr_md4_ctx_alloc(true);
+ fr_md4_update(ctx, in, inlen);
+ fr_md4_final(out, ctx);
+ fr_md4_ctx_free(&ctx);
+}
* @note license is LGPL, but largely derived from a public domain source.
*
* @file src/lib/util/md4.h
- * @brief Structures and prototypes for md4.
+ * @brief Structures and declarations for md4.
*/
RCSIDH(md4_h, "$Id$")
#include <freeradius-devel/build.h>
#include <freeradius-devel/missing.h>
-#ifdef HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
-
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
+#include <inttypes.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
-#ifdef HAVE_STDINT_H
-# include <stdint.h>
+#ifndef MD4_DIGEST_LENGTH
+# define MD4_DIGEST_LENGTH 16
#endif
-#include <string.h>
+typedef void fr_md4_ctx_t;
-#ifdef HAVE_OPENSSL_EVP_H
-# include <openssl/evp.h>
-#endif
+/* md4.c */
+/** Reset the ctx to allow reuse
+ *
+ * @param[in] ctx To reuse.
+ */
+typedef void (*fr_md4_ctx_reset_t)(fr_md4_ctx_t *ctx);
+extern fr_md4_ctx_reset_t fr_md4_ctx_reset;
-#ifndef MD4_DIGEST_LENGTH
-# define MD4_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_md4_ctx_copy_t)(fr_md4_ctx_t *dst, fr_md4_ctx_t const *src);
+extern fr_md4_ctx_copy_t fr_md4_ctx_copy;
-#ifndef HAVE_OPENSSL_EVP_H
-/*
- * The MD4 code used here and in md4.c was originally retrieved from:
- * http://www.openbsd.org/cgi-bin/cvsweb/src/include/md4.h?rev=1.12
+/** Allocation function for MD4 digest context
*
- * This code implements the MD4 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.
- * Todd C. Miller modified the MD4 code to do MD4 based on RFC 1186.
+ * @param[in] shared Whether we allocate a new context or use the thread local context.
+ * @return
+ * - An MD4 ctx.
+ * - NULL if out of memory.
+ */
+typedef fr_md4_ctx_t *(*fr_md4_ctx_alloc_t)(bool shared);
+extern fr_md4_ctx_alloc_t fr_md4_ctx_alloc;
+
+/** Free function for MD4 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 MD4 ctx to free. If the shared ctx is passed in
+ * then the ctx is reset but not freed.
*/
-# define MD4_BLOCK_LENGTH 64
-# define MD4_DIGEST_STRING_LENGTH (MD4_DIGEST_LENGTH * 2 + 1)
+typedef void (*fr_md4_ctx_free_t)(fr_md4_ctx_t **ctx);
+extern fr_md4_ctx_free_t fr_md4_ctx_free;
-typedef struct {
- uint32_t state[4]; //!< State.
- uint32_t count[2]; //!< Number of bits, mod 2^64.
- uint8_t buffer[MD4_BLOCK_LENGTH]; //!< Input buffer.
-} FR_MD4_CTX;
-void fr_md4_init(FR_MD4_CTX *ctx);
-void fr_md4_update(FR_MD4_CTX *ctx, uint8_t const *in, size_t inlen);
-void fr_md4_final(uint8_t out[static MD4_DIGEST_LENGTH], FR_MD4_CTX *ctx);
-void fr_md4_transform(uint32_t buf[static 4], uint8_t const inc[static MD4_BLOCK_LENGTH]);
-#else /* HAVE_OPENSSL_EVP_H */
-USES_APPLE_DEPRECATED_API
-#include <openssl/md4.h>
-# define FR_MD4_CTX MD4_CTX
-# define fr_md4_init MD4_Init
-# define fr_md4_update MD4_Update
-# define fr_md4_final MD4_Final
-# define fr_md4_transform MD4_Transform
-#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_md4_update_t)(fr_md4_ctx_t *ctx, uint8_t const *in, size_t inlen);
+extern fr_md4_update_t fr_md4_update;
-/* md4.c */
-void fr_md4_calc(uint8_t out[static MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen);
+/** Finalise the ctx, producing the digest
+ *
+ * @param[out] out The MD4 digest.
+ * @param[in] ctx To finalise.
+ */
+typedef void (*fr_md4_final_t)(uint8_t out[static MD4_DIGEST_LENGTH], fr_md4_ctx_t *ctx);
+extern fr_md4_final_t fr_md4_final;
+
+/** Perform a single digest operation on a single input buffer
+ *
+ */
+void fr_md4_calc(uint8_t out[static MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen);
#ifdef __cplusplus
}