compact md5 implementation
authorMarko Kreen <markokr@gmail.com>
Fri, 22 Aug 2008 17:50:37 +0000 (17:50 +0000)
committerMarko Kreen <markokr@gmail.com>
Fri, 22 Aug 2008 17:50:37 +0000 (17:50 +0000)
include/md5.h
src/md5.c

index 089e171c00052c02260e34a26b97bc4c0ff7f5b5..28f7175a2ecafb41893e6308e753dce1874499a5 100644 (file)
@@ -1,82 +1,43 @@
-/*     $PostgreSQL: pgsql/contrib/pgcrypto/md5.h,v 1.9 2005/10/15 02:49:06 momjian Exp $ */
-/*        $KAME: md5.h,v 1.3 2000/02/22 14:01:18 itojun Exp $     */
-
 /*
- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the project nor the names of its contributors
- *       may be used to endorse or promote products derived from this software
- *       without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.     IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * MD5 implementation based on RFC1321.
+ * 
+ * Copyright (c) 2008 Marko Kreen, Skype Technologies OÜ
+ * 
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#ifndef _NETINET6_MD5_H_
-#define _NETINET6_MD5_H_
-
-#define MD5_BUFLEN     64
-#define MD5_DIGEST_LENGTH 16
-
-typedef struct
-{
-       union
-       {
-               uint32_t                md5_state32[4];
-               uint8_t         md5_state8[16];
-       }                       md5_st;
-
-#define md5_sta                md5_st.md5_state32[0]
-#define md5_stb                md5_st.md5_state32[1]
-#define md5_stc                md5_st.md5_state32[2]
-#define md5_std                md5_st.md5_state32[3]
-#define md5_st8                md5_st.md5_state8
+#ifndef __MD5_H__
+#define __MD5_H__
 
-       union
-       {
-               uint64_t        md5_count64;
-               uint8_t         md5_count8[8];
-       }                       md5_count;
-#define md5_n  md5_count.md5_count64
-#define md5_n8 md5_count.md5_count8
+#define MD5_BLOCK_LENGTH       64
+#define MD5_DIGEST_LENGTH      16
 
-       unsigned        md5_i;
-       uint8_t         md5_buf[MD5_BUFLEN];
-}      md5_ctxt;
+struct md5_ctx {
+       uint64_t nbytes;
+       uint32_t a, b, c, d;
+       uint32_t buf[16];
+};
 
-extern void md5_init(md5_ctxt *);
-extern void md5_loop(md5_ctxt *, const uint8_t *, unsigned int);
-extern void md5_pad(md5_ctxt *);
-extern void md5_result(uint8_t *, md5_ctxt *);
+void md5_reset(struct md5_ctx *ctx);
+void md5_update(struct md5_ctx *ctx, const void *data, unsigned int len);
+void md5_final(uint8_t *dst, struct md5_ctx *ctx);
 
-/* compatibility with OpenSSL */
-#define MD5_CTX                md5_ctxt
-#define MD5_Init(x)    md5_init((x))
-#define MD5_Update(x, y, z)    md5_loop((x), (void*)(y), (z))
-#define MD5_Final(x, y) \
-do {                           \
-       md5_pad((y));           \
-       md5_result((x), (y));   \
-} while (0)
+#ifndef AVOID_MD5_COMPAT
+typedef struct md5_ctx MD5_CTX;
+#define MD5_Init(c) md5_reset(c)
+#define MD5_Update(c, d, l) md5_update(c, d, l)
+#define MD5_Final(d, c) md5_final(d, c)
+#endif
 
-#endif   /* ! _NETINET6_MD5_H_ */
+#endif
 
-/* vi: set ts=4: */
index e1beb3125c11011a23c38025294b8ff3b964b49c..f76dd9e1ce3786f1183d0e2de67047bde6990bef 100644 (file)
--- a/src/md5.c
+++ b/src/md5.c
-/*        $KAME: md5.c,v 1.3 2000/02/22 14:01:17 itojun Exp $     */
-
 /*
- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the project nor the names of its contributors
- *       may be used to endorse or promote products derived from this software
- *       without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.     IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $PostgreSQL: pgsql/contrib/pgcrypto/md5.c,v 1.14 2007/04/06 05:36:50 tgl Exp $
+ * MD5 implementation based on RFC1321.
+ * 
+ * Copyright (c) 2008 Marko Kreen, Skype Technologies OÜ
+ * 
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "bouncer.h"
-
+#include "system.h"
 #include "md5.h"
 
-#define SHIFT(X, s) (((X) << (s)) | ((X) >> (32 - (s))))
-
-#define F(X, Y, Z) (((X) & (Y)) | ((~X) & (Z)))
-#define G(X, Y, Z) (((X) & (Z)) | ((Y) & (~Z)))
-#define H(X, Y, Z) ((X) ^ (Y) ^ (Z))
-#define I(X, Y, Z) ((Y) ^ ((X) | (~Z)))
-
-#define ROUND1(a, b, c, d, k, s, i) \
-do { \
-       (a) = (a) + F((b), (c), (d)) + X[(k)] + T[(i)]; \
-       (a) = SHIFT((a), (s)); \
-       (a) = (b) + (a); \
-} while (0)
-
-#define ROUND2(a, b, c, d, k, s, i) \
-do { \
-       (a) = (a) + G((b), (c), (d)) + X[(k)] + T[(i)]; \
-       (a) = SHIFT((a), (s)); \
-       (a) = (b) + (a); \
-} while (0)
-
-#define ROUND3(a, b, c, d, k, s, i) \
-do { \
-       (a) = (a) + H((b), (c), (d)) + X[(k)] + T[(i)]; \
-       (a) = SHIFT((a), (s)); \
-       (a) = (b) + (a); \
-} while (0)
-
-#define ROUND4(a, b, c, d, k, s, i) \
-do { \
-       (a) = (a) + I((b), (c), (d)) + X[(k)] + T[(i)]; \
-       (a) = SHIFT((a), (s)); \
-       (a) = (b) + (a); \
-} while (0)
-
-#define Sa      7
-#define Sb     12
-#define Sc     17
-#define Sd     22
-
-#define Se      5
-#define Sf      9
-#define Sg     14
-#define Sh     20
-
-#define Si      4
-#define Sj     11
-#define Sk     16
-#define Sl     23
-
-#define Sm      6
-#define Sn     10
-#define So     15
-#define Sp     21
-
-#define MD5_A0 0x67452301
-#define MD5_B0 0xefcdab89
-#define MD5_C0 0x98badcfe
-#define MD5_D0 0x10325476
-
-/* Integer part of 4294967296 times abs(sin(i)), where i is in radians. */
-static const uint32_t T[65] = {
-       0,
-       0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
-       0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
-       0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
-       0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
-
-       0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
-       0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,
-       0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
-       0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
-
-       0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
-       0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
-       0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
-       0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
-
-       0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
-       0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
-       0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
-       0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
-};
-
-static const uint8_t md5_paddat[MD5_BUFLEN] = {
-       0x80, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0,
-};
+/*
+ * Support functions.
+ */
 
-static void md5_calc(uint8_t *, md5_ctxt *);
+#define bufpos(ctx) ((ctx)->nbytes & (MD5_BLOCK_LENGTH - 1))
 
-void
-md5_init(md5_ctxt * ctxt)
+static inline uint32_t rol(uint32_t v, int s)
 {
-       ctxt->md5_n = 0;
-       ctxt->md5_i = 0;
-       ctxt->md5_sta = MD5_A0;
-       ctxt->md5_stb = MD5_B0;
-       ctxt->md5_stc = MD5_C0;
-       ctxt->md5_std = MD5_D0;
-       memset(ctxt->md5_buf, 0, sizeof(ctxt->md5_buf));
+       return (v << s) | (v >> (32 - s));
 }
 
-void
-md5_loop(md5_ctxt * ctxt, const uint8_t *input, unsigned len)
+static inline void swap_words(uint32_t *w, int n)
 {
-       unsigned int gap,
-                               i;
-
-       ctxt->md5_n += len * 8;         /* byte to bit */
-       gap = MD5_BUFLEN - ctxt->md5_i;
-
-       if (len >= gap)
-       {
-               memmove(ctxt->md5_buf + ctxt->md5_i, input, gap);
-               md5_calc(ctxt->md5_buf, ctxt);
-
-               for (i = gap; i + MD5_BUFLEN <= len; i += MD5_BUFLEN)
-                       md5_calc((uint8_t *) (input + i), ctxt);
-
-               ctxt->md5_i = len - i;
-               memmove(ctxt->md5_buf, input + i, ctxt->md5_i);
-       }
-       else
-       {
-               memmove(ctxt->md5_buf + ctxt->md5_i, input, len);
-               ctxt->md5_i += len;
-       }
-}
-
-void
-md5_pad(md5_ctxt * ctxt)
-{
-       unsigned int gap;
-
-       /* Don't count up padding. Keep md5_n. */
-       gap = MD5_BUFLEN - ctxt->md5_i;
-       if (gap > 8)
-       {
-               memmove(ctxt->md5_buf + ctxt->md5_i, md5_paddat,
-                               gap - sizeof(ctxt->md5_n));
-       }
-       else
-       {
-               /* including gap == 8 */
-               memmove(ctxt->md5_buf + ctxt->md5_i, md5_paddat, gap);
-               md5_calc(ctxt->md5_buf, ctxt);
-               memmove(ctxt->md5_buf, md5_paddat + gap,
-                               MD5_BUFLEN - sizeof(ctxt->md5_n));
+#ifdef WORDS_BIGENDIAN
+       for (; n > 0; w++, n--) {
+               uint32_t v = rol(*w, 16);
+               *w = ((v >> 8) & 0x00FF00FF) | ((v << 8) & 0xFF00FF00);
        }
-
-       /* 8 byte word */
-#ifndef WORDS_BIGENDIAN
-       memmove(&ctxt->md5_buf[56], &ctxt->md5_n8[0], 8);
-#else
-       ctxt->md5_buf[56] = ctxt->md5_n8[7];
-       ctxt->md5_buf[57] = ctxt->md5_n8[6];
-       ctxt->md5_buf[58] = ctxt->md5_n8[5];
-       ctxt->md5_buf[59] = ctxt->md5_n8[4];
-       ctxt->md5_buf[60] = ctxt->md5_n8[3];
-       ctxt->md5_buf[61] = ctxt->md5_n8[2];
-       ctxt->md5_buf[62] = ctxt->md5_n8[1];
-       ctxt->md5_buf[63] = ctxt->md5_n8[0];
 #endif
-
-       md5_calc(ctxt->md5_buf, ctxt);
 }
 
-void
-md5_result(uint8_t *digest, md5_ctxt * ctxt)
+static inline void put_word(uint8_t *dst, uint32_t val)
 {
-       /* 4 byte words */
-#ifndef WORDS_BIGENDIAN
-       memmove(digest, &ctxt->md5_st8[0], 16);
+#ifdef WORDS_BIGENDIAN
+       dst[0] = val;
+       dst[1] = val >> 8;
+       dst[2] = val >> 16;
+       dst[3] = val >> 24;
 #else
-       digest[0] = ctxt->md5_st8[3];
-       digest[1] = ctxt->md5_st8[2];
-       digest[2] = ctxt->md5_st8[1];
-       digest[3] = ctxt->md5_st8[0];
-       digest[4] = ctxt->md5_st8[7];
-       digest[5] = ctxt->md5_st8[6];
-       digest[6] = ctxt->md5_st8[5];
-       digest[7] = ctxt->md5_st8[4];
-       digest[8] = ctxt->md5_st8[11];
-       digest[9] = ctxt->md5_st8[10];
-       digest[10] = ctxt->md5_st8[9];
-       digest[11] = ctxt->md5_st8[8];
-       digest[12] = ctxt->md5_st8[15];
-       digest[13] = ctxt->md5_st8[14];
-       digest[14] = ctxt->md5_st8[13];
-       digest[15] = ctxt->md5_st8[12];
+       memcpy(dst, &val, 4);
 #endif
 }
 
-#ifdef WORDS_BIGENDIAN
-static uint32_t X[16];
-#endif
-
-static void
-md5_calc(uint8_t *b64, md5_ctxt * ctxt)
-{
-       uint32_t        A = ctxt->md5_sta;
-       uint32_t        B = ctxt->md5_stb;
-       uint32_t        C = ctxt->md5_stc;
-       uint32_t        D = ctxt->md5_std;
+/*
+ * MD5 core.
+ */
 
-#ifndef WORDS_BIGENDIAN
-       uint32_t   *X = (uint32_t *) b64;
-#else
-       /* 4 byte words */
-       /* what a brute force but fast! */
-       uint8_t    *y = (uint8_t *) X;
+#define F(X,Y,Z) ((X & Y) | ((~X) & Z))
+#define G(X,Y,Z) ((X & Z) | (Y & (~Z)))
+#define H(X,Y,Z) (X ^ Y ^ Z)
+#define I(X,Y,Z) (Y ^ (X | (~Z)))
 
-       y[0] = b64[3];
-       y[1] = b64[2];
-       y[2] = b64[1];
-       y[3] = b64[0];
-       y[4] = b64[7];
-       y[5] = b64[6];
-       y[6] = b64[5];
-       y[7] = b64[4];
-       y[8] = b64[11];
-       y[9] = b64[10];
-       y[10] = b64[9];
-       y[11] = b64[8];
-       y[12] = b64[15];
-       y[13] = b64[14];
-       y[14] = b64[13];
-       y[15] = b64[12];
-       y[16] = b64[19];
-       y[17] = b64[18];
-       y[18] = b64[17];
-       y[19] = b64[16];
-       y[20] = b64[23];
-       y[21] = b64[22];
-       y[22] = b64[21];
-       y[23] = b64[20];
-       y[24] = b64[27];
-       y[25] = b64[26];
-       y[26] = b64[25];
-       y[27] = b64[24];
-       y[28] = b64[31];
-       y[29] = b64[30];
-       y[30] = b64[29];
-       y[31] = b64[28];
-       y[32] = b64[35];
-       y[33] = b64[34];
-       y[34] = b64[33];
-       y[35] = b64[32];
-       y[36] = b64[39];
-       y[37] = b64[38];
-       y[38] = b64[37];
-       y[39] = b64[36];
-       y[40] = b64[43];
-       y[41] = b64[42];
-       y[42] = b64[41];
-       y[43] = b64[40];
-       y[44] = b64[47];
-       y[45] = b64[46];
-       y[46] = b64[45];
-       y[47] = b64[44];
-       y[48] = b64[51];
-       y[49] = b64[50];
-       y[50] = b64[49];
-       y[51] = b64[48];
-       y[52] = b64[55];
-       y[53] = b64[54];
-       y[54] = b64[53];
-       y[55] = b64[52];
-       y[56] = b64[59];
-       y[57] = b64[58];
-       y[58] = b64[57];
-       y[59] = b64[56];
-       y[60] = b64[63];
-       y[61] = b64[62];
-       y[62] = b64[61];
-       y[63] = b64[60];
-#endif
+#define OP(fn, a, b, c, d, k, s, T_i) \
+       a = b + rol(a + fn(b, c, d) + X[k] + T_i, s)
 
-       ROUND1(A, B, C, D, 0, Sa, 1);
-       ROUND1(D, A, B, C, 1, Sb, 2);
-       ROUND1(C, D, A, B, 2, Sc, 3);
-       ROUND1(B, C, D, A, 3, Sd, 4);
-       ROUND1(A, B, C, D, 4, Sa, 5);
-       ROUND1(D, A, B, C, 5, Sb, 6);
-       ROUND1(C, D, A, B, 6, Sc, 7);
-       ROUND1(B, C, D, A, 7, Sd, 8);
-       ROUND1(A, B, C, D, 8, Sa, 9);
-       ROUND1(D, A, B, C, 9, Sb, 10);
-       ROUND1(C, D, A, B, 10, Sc, 11);
-       ROUND1(B, C, D, A, 11, Sd, 12);
-       ROUND1(A, B, C, D, 12, Sa, 13);
-       ROUND1(D, A, B, C, 13, Sb, 14);
-       ROUND1(C, D, A, B, 14, Sc, 15);
-       ROUND1(B, C, D, A, 15, Sd, 16);
+static void md5_mix(struct md5_ctx *ctx, const uint32_t *X)
+{
+       uint32_t a, b, c, d;
+
+       a = ctx->a;
+       b = ctx->b;
+       c = ctx->c;
+       d = ctx->d;
+
+       /* Round 1. */
+       OP(F, a, b, c, d, 0, 7, 0xd76aa478);
+       OP(F, d, a, b, c, 1, 12, 0xe8c7b756);
+       OP(F, c, d, a, b, 2, 17, 0x242070db);
+       OP(F, b, c, d, a, 3, 22, 0xc1bdceee);
+       OP(F, a, b, c, d, 4, 7, 0xf57c0faf);
+       OP(F, d, a, b, c, 5, 12, 0x4787c62a);
+       OP(F, c, d, a, b, 6, 17, 0xa8304613);
+       OP(F, b, c, d, a, 7, 22, 0xfd469501);
+       OP(F, a, b, c, d, 8, 7, 0x698098d8);
+       OP(F, d, a, b, c, 9, 12, 0x8b44f7af);
+       OP(F, c, d, a, b, 10, 17, 0xffff5bb1);
+       OP(F, b, c, d, a, 11, 22, 0x895cd7be);
+       OP(F, a, b, c, d, 12, 7, 0x6b901122);
+       OP(F, d, a, b, c, 13, 12, 0xfd987193);
+       OP(F, c, d, a, b, 14, 17, 0xa679438e);
+       OP(F, b, c, d, a, 15, 22, 0x49b40821);
+
+       /* Round 2. */
+       OP(G, a, b, c, d, 1, 5, 0xf61e2562);
+       OP(G, d, a, b, c, 6, 9, 0xc040b340);
+       OP(G, c, d, a, b, 11, 14, 0x265e5a51);
+       OP(G, b, c, d, a, 0, 20, 0xe9b6c7aa);
+       OP(G, a, b, c, d, 5, 5, 0xd62f105d);
+       OP(G, d, a, b, c, 10, 9, 0x02441453);
+       OP(G, c, d, a, b, 15, 14, 0xd8a1e681);
+       OP(G, b, c, d, a, 4, 20, 0xe7d3fbc8);
+       OP(G, a, b, c, d, 9, 5, 0x21e1cde6);
+       OP(G, d, a, b, c, 14, 9, 0xc33707d6);
+       OP(G, c, d, a, b, 3, 14, 0xf4d50d87);
+       OP(G, b, c, d, a, 8, 20, 0x455a14ed);
+       OP(G, a, b, c, d, 13, 5, 0xa9e3e905);
+       OP(G, d, a, b, c, 2, 9, 0xfcefa3f8);
+       OP(G, c, d, a, b, 7, 14, 0x676f02d9);
+       OP(G, b, c, d, a, 12, 20, 0x8d2a4c8a);
+
+       /* Round 3. */
+       OP(H, a, b, c, d, 5, 4, 0xfffa3942);
+       OP(H, d, a, b, c, 8, 11, 0x8771f681);
+       OP(H, c, d, a, b, 11, 16, 0x6d9d6122);
+       OP(H, b, c, d, a, 14, 23, 0xfde5380c);
+       OP(H, a, b, c, d, 1, 4, 0xa4beea44);
+       OP(H, d, a, b, c, 4, 11, 0x4bdecfa9);
+       OP(H, c, d, a, b, 7, 16, 0xf6bb4b60);
+       OP(H, b, c, d, a, 10, 23, 0xbebfbc70);
+       OP(H, a, b, c, d, 13, 4, 0x289b7ec6);
+       OP(H, d, a, b, c, 0, 11, 0xeaa127fa);
+       OP(H, c, d, a, b, 3, 16, 0xd4ef3085);
+       OP(H, b, c, d, a, 6, 23, 0x04881d05);
+       OP(H, a, b, c, d, 9, 4, 0xd9d4d039);
+       OP(H, d, a, b, c, 12, 11, 0xe6db99e5);
+       OP(H, c, d, a, b, 15, 16, 0x1fa27cf8);
+       OP(H, b, c, d, a, 2, 23, 0xc4ac5665);
+
+       /* Round 4. */
+       OP(I, a, b, c, d, 0, 6, 0xf4292244);
+       OP(I, d, a, b, c, 7, 10, 0x432aff97);
+       OP(I, c, d, a, b, 14, 15, 0xab9423a7);
+       OP(I, b, c, d, a, 5, 21, 0xfc93a039);
+       OP(I, a, b, c, d, 12, 6, 0x655b59c3);
+       OP(I, d, a, b, c, 3, 10, 0x8f0ccc92);
+       OP(I, c, d, a, b, 10, 15, 0xffeff47d);
+       OP(I, b, c, d, a, 1, 21, 0x85845dd1);
+       OP(I, a, b, c, d, 8, 6, 0x6fa87e4f);
+       OP(I, d, a, b, c, 15, 10, 0xfe2ce6e0);
+       OP(I, c, d, a, b, 6, 15, 0xa3014314);
+       OP(I, b, c, d, a, 13, 21, 0x4e0811a1);
+       OP(I, a, b, c, d, 4, 6, 0xf7537e82);
+       OP(I, d, a, b, c, 11, 10, 0xbd3af235);
+       OP(I, c, d, a, b, 2, 15, 0x2ad7d2bb);
+       OP(I, b, c, d, a, 9, 21, 0xeb86d391);
+
+       ctx->a += a;
+       ctx->b += b;
+       ctx->c += c;
+       ctx->d += d;
+}
 
-       ROUND2(A, B, C, D, 1, Se, 17);
-       ROUND2(D, A, B, C, 6, Sf, 18);
-       ROUND2(C, D, A, B, 11, Sg, 19);
-       ROUND2(B, C, D, A, 0, Sh, 20);
-       ROUND2(A, B, C, D, 5, Se, 21);
-       ROUND2(D, A, B, C, 10, Sf, 22);
-       ROUND2(C, D, A, B, 15, Sg, 23);
-       ROUND2(B, C, D, A, 4, Sh, 24);
-       ROUND2(A, B, C, D, 9, Se, 25);
-       ROUND2(D, A, B, C, 14, Sf, 26);
-       ROUND2(C, D, A, B, 3, Sg, 27);
-       ROUND2(B, C, D, A, 8, Sh, 28);
-       ROUND2(A, B, C, D, 13, Se, 29);
-       ROUND2(D, A, B, C, 2, Sf, 30);
-       ROUND2(C, D, A, B, 7, Sg, 31);
-       ROUND2(B, C, D, A, 12, Sh, 32);
+/*
+ * Public API.
+ */
 
-       ROUND3(A, B, C, D, 5, Si, 33);
-       ROUND3(D, A, B, C, 8, Sj, 34);
-       ROUND3(C, D, A, B, 11, Sk, 35);
-       ROUND3(B, C, D, A, 14, Sl, 36);
-       ROUND3(A, B, C, D, 1, Si, 37);
-       ROUND3(D, A, B, C, 4, Sj, 38);
-       ROUND3(C, D, A, B, 7, Sk, 39);
-       ROUND3(B, C, D, A, 10, Sl, 40);
-       ROUND3(A, B, C, D, 13, Si, 41);
-       ROUND3(D, A, B, C, 0, Sj, 42);
-       ROUND3(C, D, A, B, 3, Sk, 43);
-       ROUND3(B, C, D, A, 6, Sl, 44);
-       ROUND3(A, B, C, D, 9, Si, 45);
-       ROUND3(D, A, B, C, 12, Sj, 46);
-       ROUND3(C, D, A, B, 15, Sk, 47);
-       ROUND3(B, C, D, A, 2, Sl, 48);
+void md5_reset(struct md5_ctx *ctx)
+{
+       ctx->nbytes = 0;
+       ctx->a = 0x67452301;
+       ctx->b = 0xefcdab89;
+       ctx->c = 0x98badcfe;
+       ctx->d = 0x10325476;
+}
 
-       ROUND4(A, B, C, D, 0, Sm, 49);
-       ROUND4(D, A, B, C, 7, Sn, 50);
-       ROUND4(C, D, A, B, 14, So, 51);
-       ROUND4(B, C, D, A, 5, Sp, 52);
-       ROUND4(A, B, C, D, 12, Sm, 53);
-       ROUND4(D, A, B, C, 3, Sn, 54);
-       ROUND4(C, D, A, B, 10, So, 55);
-       ROUND4(B, C, D, A, 1, Sp, 56);
-       ROUND4(A, B, C, D, 8, Sm, 57);
-       ROUND4(D, A, B, C, 15, Sn, 58);
-       ROUND4(C, D, A, B, 6, So, 59);
-       ROUND4(B, C, D, A, 13, Sp, 60);
-       ROUND4(A, B, C, D, 4, Sm, 61);
-       ROUND4(D, A, B, C, 11, Sn, 62);
-       ROUND4(C, D, A, B, 2, So, 63);
-       ROUND4(B, C, D, A, 9, Sp, 64);
+void md5_update(struct md5_ctx *ctx, const void *data, unsigned int len)
+{
+       unsigned int n;
+       const uint8_t *ptr = data;
+       uint8_t *buf = (uint8_t *)ctx->buf;
+
+       while (len > 0) {
+               n = MD5_BLOCK_LENGTH - bufpos(ctx);
+               if (n > len)
+                       n = len;
+               memcpy(buf + bufpos(ctx), ptr, n);
+               ptr += n;
+               len -= n;
+               ctx->nbytes += n;
+               if (bufpos(ctx) == 0) {
+                       swap_words(ctx->buf, 16);
+                       md5_mix(ctx, ctx->buf);
+               }
+       }
+}
 
-       ctxt->md5_sta += A;
-       ctxt->md5_stb += B;
-       ctxt->md5_stc += C;
-       ctxt->md5_std += D;
+void md5_final(uint8_t *dst, struct md5_ctx *ctx)
+{
+       static const uint8_t padding[MD5_BLOCK_LENGTH] = { 0x80 };
+       uint64_t final_len = ctx->nbytes * 8;
+       int pad_len, pos = bufpos(ctx);
+
+       /* add padding */
+       pad_len = MD5_BLOCK_LENGTH - 8 - pos;
+       if (pad_len <= 0)
+               pad_len += MD5_BLOCK_LENGTH;
+       md5_update(ctx, padding, pad_len);
+
+       /* add length directly */
+       swap_words(ctx->buf, 14);
+       ctx->buf[14] = final_len;
+       ctx->buf[15] = final_len >> 32;
+
+       /* final result */
+       md5_mix(ctx, ctx->buf);
+       put_word(dst, ctx->a);
+       put_word(dst + 4, ctx->b);
+       put_word(dst + 8, ctx->c);
+       put_word(dst + 12, ctx->d);
 }
 
-/* vi: set ts=4: */