[RFC PATCH] Add SHA1 support

Sebastian Andrzej Siewior rsync at ml.breakpoint.cc
Sun Feb 9 22:19:48 UTC 2020


From: Sebastian Andrzej Siewior <sebastian at breakpoint.cc>

This is a huge all-in-one patch and deserves a little cleanup and
splitting. However, I wanted to get it out here for some feedback.

My primar motivation to use SHA1 for checksumming (by default) instead
of MD5 is not the additional security bits but performance. On a decent
x86 box the SHA1 performance is almost the same as MD5's but with
acceleration it outperforms MD5.

The other alternative would be to go for xxHash64 [0] which has the
superior performance but provides a non-cryptographic hash so I though
SHA1 would be better here.

For linking against OpenSSL as of today the rsync license would need an
"OpenSSL exception" [1]. The master branch of OpenSSL is licensed under
the Apache License 2.0 so we could wait until 3.0 is released and use
the C version of the algorithm in the meantime.

Here are numbers from a ryzen test box:
small file:
|$ dd if=/dev/zero of=/dev/shm/out bs=1073741824 count=1
|1+0 records in
|1+0 records out
|1073741824 bytes (1,1 GB, 1,0 GiB) copied, 0,503252 s, 2,1 GB/s

Old hash:
|$ time ./rsync -c /dev/shm/out --checksum-choice=md4
|-rw-r--r--  1,073,741,824 2020/02/08 16:34:42 out
|
|real    0m1,064s
|user    0m0,984s
|sys     0m0,080s

MD5 from openssl (should match built-in speed):
|$ time ./rsync -c /dev/shm/out --checksum-choice=md5
|-rw-r--r--  1,073,741,824 2020/02/08 16:34:42 out
|
|real    0m1,433s
|user    0m1,293s
|sys     0m0,140s

SHA1 from openssl:
|$ time ./rsync -c /dev/shm/out --checksum-choice=sha1
|-rw-r--r--  1,073,741,824 2020/02/08 16:34:42 out
|
|real    0m0,619s
|user    0m0,524s
|sys     0m0,096s

SHA1 from the built-in code:
|time ./rsync -c /dev/shm/out --checksum-choice=sha1
|-rw-r--r--  1,073,741,824 2020/02/08 16:34:42 out
|
|real    0m1,561s
|user    0m1,465s
|sys     0m0,096s


[1] https://opensource.stackexchange.com/questions/2233/gpl-v3-with-openssl-exception
[0] https://github.com/Cyan4973/xxHash

Signed-off-by: Sebastian Andrzej Siewior <sebastian at breakpoint.cc>
---
 Makefile.in       |   4 +-
 checksum.c        | 144 +++++++++++++----
 configure.ac      |   5 +
 lib/md32_common.h | 258 +++++++++++++++++++++++++++++
 lib/md5.c         |  15 +-
 lib/mdigest.h     |  77 ++++++++-
 lib/sha1.c        |  19 +++
 lib/sha1.h        |  20 +++
 lib/sha_local.h   | 401 ++++++++++++++++++++++++++++++++++++++++++++++
 main.c            |   2 +
 rsync.h           |   2 +-
 11 files changed, 902 insertions(+), 45 deletions(-)
 create mode 100644 lib/md32_common.h
 create mode 100644 lib/sha1.c
 create mode 100644 lib/sha1.h
 create mode 100644 lib/sha_local.h

diff --git a/Makefile.in b/Makefile.in
index 9bb977eb6b0a8..a390afe4ed829 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -32,7 +32,7 @@ VERSION=@RSYNC_VERSION@
 GENFILES=configure.sh aclocal.m4 config.h.in proto.h proto.h-tstamp rsync.1 rsyncd.conf.5
 HEADERS=byteorder.h config.h errcode.h proto.h rsync.h ifuncs.h itypes.h inums.h \
 	lib/pool_alloc.h
-LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \
+LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o lib/sha1.o \
 	lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattrs.o @LIBOBJS@
 zlib_OBJS=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
 	zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
diff --git a/checksum.c b/checksum.c
index 3295252ba0120..77c36b59c93ec 100644
--- a/checksum.c
+++ b/checksum.c
@@ -32,6 +32,7 @@ extern char *checksum_choice;
 #define CSUM_MD4_OLD 3
 #define CSUM_MD4 4
 #define CSUM_MD5 5
+#define CSUM_SHA1 6
 
 int xfersum_type = 0; /* used for the file transfer checksums */
 int checksum_type = 0; /* used for the pre-transfer (--checksum) checksums */
@@ -54,6 +55,8 @@ int parse_csum_name(const char *name, int len)
 		len = strlen(name);
 
 	if (!name || (len == 4 && strncasecmp(name, "auto", 4) == 0)) {
+		if (protocol_version >= 31)
+			return CSUM_SHA1;
 		if (protocol_version >= 30)
 			return CSUM_MD5;
 		if (protocol_version >= 27)
@@ -68,6 +71,8 @@ int parse_csum_name(const char *name, int len)
 		return CSUM_MD5;
 	if (len == 4 && strncasecmp(name, "none", 4) == 0)
 		return CSUM_NONE;
+	if (len == 4 && strncasecmp(name, "sha1", 4) == 0)
+		return CSUM_SHA1;
 
 	rprintf(FERROR, "unknown checksum name: %s\n", name);
 	exit_cleanup(RERR_UNSUPPORTED);
@@ -88,6 +93,8 @@ int csum_len_for_type(int cst, BOOL flist_csum)
 		return MD4_DIGEST_LEN;
 	  case CSUM_MD5:
 		return MD5_DIGEST_LEN;
+	  case CSUM_SHA1:
+		return SHA1_DIGEST_LEN;
 	  default: /* paranoia to prevent missing case values */
 		exit_cleanup(RERR_UNSUPPORTED);
 	}
@@ -121,30 +128,48 @@ uint32 get_checksum1(char *buf1, int32 len)
     return (s1 & 0xffff) + (s2 << 16);
 }
 
+static void get_checksum2_lib(void *buf, int32 len, void *sum)
+{
+	uchar seedbuf[4];
+	MDLIB_MD_CTX *mdctx;
+
+	mdctx = mdlib_new_ctx();
+	if (!mdctx)
+		out_of_memory("get_checksum2");
+
+	if (xfersum_type == CSUM_MD5)
+		mdlib_init_md5(mdctx);
+	else if (xfersum_type == CSUM_SHA1)
+		mdlib_init_sha1(mdctx);
+	else
+		out_of_memory("wrong checksum");
+
+	if (proper_seed_order) {
+		if (checksum_seed) {
+			SIVALu(seedbuf, 0, checksum_seed);
+			mdlib_update(mdctx, seedbuf, 4);
+		}
+		mdlib_update(mdctx, buf, len);
+	} else {
+		mdlib_update(mdctx, buf, len);
+		if (checksum_seed) {
+			SIVALu(seedbuf, 0, checksum_seed);
+			mdlib_update(mdctx, seedbuf, 4);
+		}
+	}
+	mdlib_finalize(mdctx, sum);
+	mdlib_free_ctx(mdctx);
+}
+
 void get_checksum2(char *buf, int32 len, char *sum)
 {
 	md_context m;
 
 	switch (xfersum_type) {
-	  case CSUM_MD5: {
-		uchar seedbuf[4];
-		md5_begin(&m);
-		if (proper_seed_order) {
-			if (checksum_seed) {
-				SIVALu(seedbuf, 0, checksum_seed);
-				md5_update(&m, seedbuf, 4);
-			}
-			md5_update(&m, (uchar *)buf, len);
-		} else {
-			md5_update(&m, (uchar *)buf, len);
-			if (checksum_seed) {
-				SIVALu(seedbuf, 0, checksum_seed);
-				md5_update(&m, seedbuf, 4);
-			}
-		}
-		md5_result(&m, (uchar *)sum);
+	  case CSUM_SHA1:
+	  case CSUM_MD5:
+		get_checksum2_lib(buf, len, sum);
 		break;
-	  }
 	  case CSUM_MD4:
 	  case CSUM_MD4_OLD:
 	  case CSUM_MD4_BUSTED:
@@ -190,6 +215,51 @@ void get_checksum2(char *buf, int32 len, char *sum)
 	}
 }
 
+static NORETURN void openssl_failure(const char *str)
+{
+	rprintf(FERROR, "ERROR: An OpenSSL function failed %s [%s]\n", str, who_am_i());
+	exit_cleanup(RERR_UNSUPPORTED);
+}
+
+static void compute_lib_hash(struct map_struct *buf, OFF_T len, void *sum)
+{
+	MDLIB_MD_CTX *mdctx;
+	OFF_T i;
+	int remainder;
+	int ret;
+
+	mdctx = mdlib_new_ctx();
+	if (!mdctx)
+		out_of_memory("HERE");
+
+	if (checksum_type == CSUM_MD5)
+		mdlib_init_md5(mdctx);
+	else if (checksum_type == CSUM_SHA1)
+		mdlib_init_sha1(mdctx);
+	else
+		out_of_memory("wrong checksum");
+
+	for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK) {
+		ret = mdlib_update(mdctx, map_ptr(buf, i, CSUM_CHUNK),
+				       CSUM_CHUNK);
+		if (!ret)
+			openssl_failure(__func__);
+	}
+
+	remainder = (len - i);
+	if (remainder > 0) {
+		ret = mdlib_update(mdctx, map_ptr(buf, i, remainder),
+				 remainder);
+		if (!ret)
+			openssl_failure(__func__);
+	}
+
+	ret = mdlib_finalize(mdctx, sum);
+	if (!ret)
+		openssl_failure(__func__);
+	mdlib_free_ctx(mdctx);
+}
+
 void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
 {
 	struct map_struct *buf;
@@ -207,19 +277,9 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
 	buf = map_file(fd, len, MAX_MAP_SIZE, CSUM_CHUNK);
 
 	switch (checksum_type) {
+	  case CSUM_SHA1:
 	  case CSUM_MD5:
-		md5_begin(&m);
-
-		for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK) {
-			md5_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK),
-				   CSUM_CHUNK);
-		}
-
-		remainder = (int32)(len - i);
-		if (remainder > 0)
-			md5_update(&m, (uchar *)map_ptr(buf, i, remainder), remainder);
-
-		md5_result(&m, (uchar *)sum);
+		compute_lib_hash(buf, len, sum);
 		break;
 	  case CSUM_MD4:
 	  case CSUM_MD4_OLD:
@@ -254,6 +314,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
 static int32 sumresidue;
 static md_context md;
 static int cursum_type;
+static MDLIB_MD_CTX *md_sum;
 
 void sum_init(int csum_type, int seed)
 {
@@ -264,8 +325,21 @@ void sum_init(int csum_type, int seed)
 	cursum_type = csum_type;
 
 	switch (csum_type) {
+	  case CSUM_SHA1:
 	  case CSUM_MD5:
-		md5_begin(&md);
+
+		if (!md_sum)
+			md_sum = mdlib_new_ctx();
+		if (!md_sum)
+			out_of_memory("HERE");
+
+		if (csum_type == CSUM_MD5)
+			mdlib_init_md5(md_sum);
+
+		else if (csum_type == CSUM_SHA1)
+			mdlib_init_sha1(md_sum);
+		else
+			out_of_memory("bad");
 		break;
 	  case CSUM_MD4:
 		mdfour_begin(&md);
@@ -297,8 +371,10 @@ void sum_init(int csum_type, int seed)
 void sum_update(const char *p, int32 len)
 {
 	switch (cursum_type) {
+	  case CSUM_SHA1:
 	  case CSUM_MD5:
-		md5_update(&md, (uchar *)p, len);
+		if (!mdlib_update(md_sum, (void *)p, len))
+			openssl_failure(__func__);
 		break;
 	  case CSUM_MD4:
 	  case CSUM_MD4_OLD:
@@ -342,8 +418,10 @@ void sum_update(const char *p, int32 len)
 int sum_end(char *sum)
 {
 	switch (cursum_type) {
+	  case CSUM_SHA1:
 	  case CSUM_MD5:
-		md5_result(&md, (uchar *)sum);
+		if (!mdlib_finalize(md_sum, (void *)sum))
+			openssl_failure(__func__);
 		break;
 	  case CSUM_MD4:
 	  case CSUM_MD4_OLD:
diff --git a/configure.ac b/configure.ac
index 12db93570d78a..bdd010044bb61 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1056,6 +1056,11 @@ AS_IF([test "x$want_libzstd" = "xyes"],
 AC_SUBST([LIBZSTD_CFLAGS])
 AC_SUBST([LIBZSTD_LIBS])
 
+AC_CHECK_FUNCS(OPENSSL_init_crypto)
+if test x"$ac_cv_func_OPENSSL_init_crypto" = x"no"; then
+    AC_CHECK_LIB(crypto, OPENSSL_init_crypto, [AC_DEFINE([HAVE_OPENSSL], [1], [Use OpenSSL])])
+fi
+
 #################################################
 # check for extended attribute support
 AC_MSG_CHECKING(whether to support extended attributes)
diff --git a/lib/md32_common.h b/lib/md32_common.h
new file mode 100644
index 0000000000000..edb61340a1138
--- /dev/null
+++ b/lib/md32_common.h
@@ -0,0 +1,258 @@
+/*
+ * Copyright 1999-2018 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * This is a generic 32 bit "collector" for message digest algorithms.
+ * Whenever needed it collects input character stream into chunks of
+ * 32 bit values and invokes a block function that performs actual hash
+ * calculations.
+ *
+ * Porting guide.
+ *
+ * Obligatory macros:
+ *
+ * DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN
+ *      this macro defines byte order of input stream.
+ * HASH_CBLOCK
+ *      size of a unit chunk HASH_BLOCK operates on.
+ * HASH_LONG
+ *      has to be at least 32 bit wide.
+ * HASH_CTX
+ *      context structure that at least contains following
+ *      members:
+ *              typedef struct {
+ *                      ...
+ *                      HASH_LONG       Nl,Nh;
+ *                      either {
+ *                      HASH_LONG       data[HASH_LBLOCK];
+ *                      unsigned char   data[HASH_CBLOCK];
+ *                      };
+ *                      unsigned int    num;
+ *                      ...
+ *                      } HASH_CTX;
+ *      data[] vector is expected to be zeroed upon first call to
+ *      HASH_UPDATE.
+ * HASH_UPDATE
+ *      name of "Update" function, implemented here.
+ * HASH_TRANSFORM
+ *      name of "Transform" function, implemented here.
+ * HASH_FINAL
+ *      name of "Final" function, implemented here.
+ * HASH_BLOCK_DATA_ORDER
+ *      name of "block" function capable of treating *unaligned* input
+ *      message in original (data) byte order, implemented externally.
+ * HASH_MAKE_STRING
+ *      macro converting context variables to an ASCII hash string.
+ *
+ * MD5 example:
+ *
+ *      #define DATA_ORDER_IS_LITTLE_ENDIAN
+ *
+ *      #define HASH_LONG               MD5_LONG
+ *      #define HASH_CTX                MD5_CTX
+ *      #define HASH_CBLOCK             MD5_CBLOCK
+ *      #define HASH_UPDATE             MD5_Update
+ *      #define HASH_TRANSFORM          MD5_Transform
+ *      #define HASH_FINAL              MD5_Final
+ *      #define HASH_BLOCK_DATA_ORDER   md5_block_data_order
+ */
+
+#include <openssl/crypto.h>
+
+#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+# error "DATA_ORDER must be defined!"
+#endif
+
+#ifndef HASH_CBLOCK
+# error "HASH_CBLOCK must be defined!"
+#endif
+#ifndef HASH_LONG
+# error "HASH_LONG must be defined!"
+#endif
+#ifndef HASH_CTX
+# error "HASH_CTX must be defined!"
+#endif
+
+#ifndef HASH_UPDATE
+# error "HASH_UPDATE must be defined!"
+#endif
+#ifndef HASH_TRANSFORM
+# error "HASH_TRANSFORM must be defined!"
+#endif
+#ifndef HASH_FINAL
+# error "HASH_FINAL must be defined!"
+#endif
+
+#ifndef HASH_BLOCK_DATA_ORDER
+# error "HASH_BLOCK_DATA_ORDER must be defined!"
+#endif
+
+#define ROTATE(a,n)     (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))
+
+#if defined(DATA_ORDER_IS_BIG_ENDIAN)
+
+# define HOST_c2l(c,l)  (l =(((unsigned long)(*((c)++)))<<24),          \
+                         l|=(((unsigned long)(*((c)++)))<<16),          \
+                         l|=(((unsigned long)(*((c)++)))<< 8),          \
+                         l|=(((unsigned long)(*((c)++)))    )           )
+# define HOST_l2c(l,c)  (*((c)++)=(unsigned char)(((l)>>24)&0xff),      \
+                         *((c)++)=(unsigned char)(((l)>>16)&0xff),      \
+                         *((c)++)=(unsigned char)(((l)>> 8)&0xff),      \
+                         *((c)++)=(unsigned char)(((l)    )&0xff),      \
+                         l)
+
+#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+
+# define HOST_c2l(c,l)  (l =(((unsigned long)(*((c)++)))    ),          \
+                         l|=(((unsigned long)(*((c)++)))<< 8),          \
+                         l|=(((unsigned long)(*((c)++)))<<16),          \
+                         l|=(((unsigned long)(*((c)++)))<<24)           )
+# define HOST_l2c(l,c)  (*((c)++)=(unsigned char)(((l)    )&0xff),      \
+                         *((c)++)=(unsigned char)(((l)>> 8)&0xff),      \
+                         *((c)++)=(unsigned char)(((l)>>16)&0xff),      \
+                         *((c)++)=(unsigned char)(((l)>>24)&0xff),      \
+                         l)
+
+#endif
+
+/*
+ * Time for some action :-)
+ */
+
+static int HASH_UPDATE(HASH_CTX *c, const void *data_, size_t len)
+{
+    const unsigned char *data = data_;
+    unsigned char *p;
+    HASH_LONG l;
+    size_t n;
+
+    if (len == 0)
+        return 1;
+
+    l = (c->Nl + (((HASH_LONG) len) << 3)) & 0xffffffffUL;
+    if (l < c->Nl)              /* overflow */
+        c->Nh++;
+    c->Nh += (HASH_LONG) (len >> 29); /* might cause compiler warning on
+                                       * 16-bit */
+    c->Nl = l;
+
+    n = c->num;
+    if (n != 0) {
+        p = (unsigned char *)c->data;
+
+        if (len >= HASH_CBLOCK || len + n >= HASH_CBLOCK) {
+            memcpy(p + n, data, HASH_CBLOCK - n);
+            HASH_BLOCK_DATA_ORDER(c, p, 1);
+            n = HASH_CBLOCK - n;
+            data += n;
+            len -= n;
+            c->num = 0;
+            /*
+             * We use memset rather than OPENSSL_cleanse() here deliberately.
+             * Using OPENSSL_cleanse() here could be a performance issue. It
+             * will get properly cleansed on finalisation so this isn't a
+             * security problem.
+             */
+            memset(p, 0, HASH_CBLOCK); /* keep it zeroed */
+        } else {
+            memcpy(p + n, data, len);
+            c->num += (unsigned int)len;
+            return 1;
+        }
+    }
+
+    n = len / HASH_CBLOCK;
+    if (n > 0) {
+        HASH_BLOCK_DATA_ORDER(c, data, n);
+        n *= HASH_CBLOCK;
+        data += n;
+        len -= n;
+    }
+
+    if (len != 0) {
+        p = (unsigned char *)c->data;
+        c->num = (unsigned int)len;
+        memcpy(p, data, len);
+    }
+    return 1;
+}
+
+#if 0
+static void HASH_TRANSFORM(HASH_CTX *c, const unsigned char *data)
+{
+    HASH_BLOCK_DATA_ORDER(c, data, 1);
+}
+#endif
+
+static int HASH_FINAL(unsigned char *md, HASH_CTX *c)
+{
+    unsigned char *p = (unsigned char *)c->data;
+    size_t n = c->num;
+
+    p[n] = 0x80;                /* there is always room for one */
+    n++;
+
+    if (n > (HASH_CBLOCK - 8)) {
+        memset(p + n, 0, HASH_CBLOCK - n);
+        n = 0;
+        HASH_BLOCK_DATA_ORDER(c, p, 1);
+    }
+    memset(p + n, 0, HASH_CBLOCK - 8 - n);
+
+    p += HASH_CBLOCK - 8;
+#if   defined(DATA_ORDER_IS_BIG_ENDIAN)
+    (void)HOST_l2c(c->Nh, p);
+    (void)HOST_l2c(c->Nl, p);
+#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+    (void)HOST_l2c(c->Nl, p);
+    (void)HOST_l2c(c->Nh, p);
+#endif
+    p -= HASH_CBLOCK;
+    HASH_BLOCK_DATA_ORDER(c, p, 1);
+    c->num = 0;
+    memset(p, 0, HASH_CBLOCK);
+
+#ifndef HASH_MAKE_STRING
+# error "HASH_MAKE_STRING must be defined!"
+#else
+    HASH_MAKE_STRING(c, md);
+#endif
+
+    return 1;
+}
+
+#ifndef MD32_REG_T
+# if defined(__alpha) || defined(__sparcv9) || defined(__mips)
+#  define MD32_REG_T long
+/*
+ * This comment was originally written for MD5, which is why it
+ * discusses A-D. But it basically applies to all 32-bit digests,
+ * which is why it was moved to common header file.
+ *
+ * In case you wonder why A-D are declared as long and not
+ * as MD5_LONG. Doing so results in slight performance
+ * boost on LP64 architectures. The catch is we don't
+ * really care if 32 MSBs of a 64-bit register get polluted
+ * with eventual overflows as we *save* only 32 LSBs in
+ * *either* case. Now declaring 'em long excuses the compiler
+ * from keeping 32 MSBs zeroed resulting in 13% performance
+ * improvement under SPARC Solaris7/64 and 5% under AlphaLinux.
+ * Well, to be honest it should say that this *prevents*
+ * performance degradation.
+ */
+# else
+/*
+ * Above is not absolute and there are LP64 compilers that
+ * generate better code if MD32_REG_T is defined int. The above
+ * pre-processor condition reflects the circumstances under which
+ * the conclusion was made and is subject to further extension.
+ */
+#  define MD32_REG_T int
+# endif
+#endif
diff --git a/lib/md5.c b/lib/md5.c
index 4baa9638e9ce4..b741dfbebb4fa 100644
--- a/lib/md5.c
+++ b/lib/md5.c
@@ -207,13 +207,6 @@ void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN])
 	SIVALu(digest, 12, ctx->D);
 }
 
-void get_md5(uchar *out, const uchar *input, int n)
-{
-	md_context ctx;
-	md5_begin(&ctx);
-	md5_update(&ctx, input, n);
-	md5_result(&ctx, out);
-}
 
 #ifdef TEST_MD5
 
@@ -244,6 +237,14 @@ static struct {
  { NULL, NULL }
 };
 
+static void get_md5(uchar *out, const uchar *input, int n)
+{
+	md_context ctx;
+	md5_begin(&ctx);
+	md5_update(&ctx, input, n);
+	md5_result(&ctx, out);
+}
+
 int main(int argc, char *argv[])
 {
 	FILE *f;
diff --git a/lib/mdigest.h b/lib/mdigest.h
index e0e33ed364c83..6f5ec33ef3b33 100644
--- a/lib/mdigest.h
+++ b/lib/mdigest.h
@@ -2,7 +2,8 @@
 
 #define MD4_DIGEST_LEN 16
 #define MD5_DIGEST_LEN 16
-#define MAX_DIGEST_LEN MD5_DIGEST_LEN
+#define SHA1_DIGEST_LEN 20
+#define MAX_DIGEST_LEN SHA1_DIGEST_LEN
 
 #define CSUM_CHUNK 64
 
@@ -19,8 +20,80 @@ void mdfour_result(md_context *md, uchar digest[MD4_DIGEST_LEN]);
 
 void get_mdfour(uchar digest[MD4_DIGEST_LEN], const uchar *in, int length);
 
+#ifdef HAVE_OPENSSL
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+
+#define MDLIB_MD_CTX			EVP_MD_CTX
+
+#define mdlib_initialize()		OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
+
+#define mdlib_new_ctx()			EVP_MD_CTX_new()
+#define mdlib_free_ctx(_ctx)		EVP_MD_CTX_free(_ctx)
+
+#define mdlib_init_md5(_ctx)		EVP_DigestInit_ex(_ctx, EVP_md5(), NULL)
+#define mdlib_init_sha1(_ctx)		EVP_DigestInit_ex(_ctx, EVP_sha1(), NULL)
+
+#define mdlib_update(_ctx, _buf, _len)	EVP_DigestUpdate(_ctx, _buf, _len)
+#define mdlib_finalize(_ctx, _out)	EVP_DigestFinal_ex(_ctx, _out, NULL)
+
+#else
+
+#include "lib/sha1.h"
+
+struct md_lib_ctx {
+	int mode;
+	union {
+		md_context md5;
+		SHA_CTX	sha1;
+	};
+};
+
 void md5_begin(md_context *ctx);
 void md5_update(md_context *ctx, const uchar *input, uint32 length);
 void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN]);
 
-void get_md5(uchar digest[MD5_DIGEST_LEN], const uchar *input, int n);
+void sha1_begin(SHA_CTX *ctx);
+void sha1_update(SHA_CTX *ctx, const void *input, unsigned int length);
+void sha1_result(SHA_CTX *ctx, void *digest);
+
+#define MDLIB_MD_CTX			struct md_lib_ctx
+
+#define mdlib_initialize()		do { } while (0)
+
+#define mdlib_new_ctx()			malloc(sizeof(struct md_lib_ctx))
+#define mdlib_free_ctx(_ctx)		free(_ctx)
+
+static inline int mdlib_init_md5(struct md_lib_ctx *ctx)
+{
+	md5_begin(&ctx->md5);
+	ctx->mode = 0;
+	return 1;
+}
+
+static inline int mdlib_init_sha1(struct md_lib_ctx *ctx)
+{
+	sha1_begin(&ctx->sha1);
+	ctx->mode = 1;
+	return 1;
+}
+
+static inline int mdlib_update(struct md_lib_ctx *ctx, void *buf, unsigned int len)
+{
+	if (ctx->mode == 0)
+		md5_update(&ctx->md5, buf, len);
+	else if (ctx->mode == 1)
+		sha1_update(&ctx->sha1, buf, len);
+	return 1;
+}
+
+static inline int mdlib_finalize(struct md_lib_ctx *ctx, void *out)
+{
+	if (ctx->mode == 0)
+		md5_result(&ctx->md5, out);
+	else if (ctx->mode == 1)
+		sha1_result(&ctx->sha1, out);
+	return 1;
+}
+
+#endif
diff --git a/lib/sha1.c b/lib/sha1.c
new file mode 100644
index 0000000000000..e1cc3fccf2552
--- /dev/null
+++ b/lib/sha1.c
@@ -0,0 +1,19 @@
+#include "rsync.h"
+#include "sha_local.h"
+
+#ifndef HAVE_OPENSSL
+void sha1_begin(SHA_CTX *ctx)
+{
+	SHA1_Init(ctx);
+}
+
+void sha1_update(SHA_CTX *ctx, const void *input, unsigned int length)
+{
+	HASH_UPDATE(ctx, input, length);
+}
+
+void sha1_result(SHA_CTX *ctx, void *digest)
+{
+	HASH_FINAL(digest, ctx);
+}
+#endif
diff --git a/lib/sha1.h b/lib/sha1.h
new file mode 100644
index 0000000000000..ea979e4a31ef7
--- /dev/null
+++ b/lib/sha1.h
@@ -0,0 +1,20 @@
+#ifndef __LIB__SHA1__
+#define __LIB__SHA1__
+
+# define SHA_LONG unsigned int
+# define SHA_LBLOCK      16
+# define SHA_CBLOCK      (SHA_LBLOCK*4)/* SHA treats input data as a
+					* contiguous array of 32 bit wide
+					* big-endian values. */
+# define SHA_LAST_BLOCK  (SHA_CBLOCK-8)
+# define SHA_DIGEST_LENGTH 20
+
+
+typedef struct SHAstate_st {
+	SHA_LONG h0, h1, h2, h3, h4;
+	SHA_LONG Nl, Nh;
+	SHA_LONG data[SHA_LBLOCK];
+	unsigned int num;
+} SHA_CTX;
+
+#endif
diff --git a/lib/sha_local.h b/lib/sha_local.h
new file mode 100644
index 0000000000000..58626ee3013b8
--- /dev/null
+++ b/lib/sha_local.h
@@ -0,0 +1,401 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "sha1.h"
+
+#define DATA_ORDER_IS_BIG_ENDIAN
+
+#define HASH_LONG               SHA_LONG
+#define HASH_CTX                SHA_CTX
+#define HASH_CBLOCK             SHA_CBLOCK
+#define HASH_MAKE_STRING(c,s)   do {    \
+        unsigned long ll;               \
+        ll=(c)->h0; (void)HOST_l2c(ll,(s));     \
+        ll=(c)->h1; (void)HOST_l2c(ll,(s));     \
+        ll=(c)->h2; (void)HOST_l2c(ll,(s));     \
+        ll=(c)->h3; (void)HOST_l2c(ll,(s));     \
+        ll=(c)->h4; (void)HOST_l2c(ll,(s));     \
+        } while (0)
+
+#define HASH_UPDATE                     SHA1_Update
+#define HASH_TRANSFORM                  SHA1_Transform
+#define HASH_FINAL                      SHA1_Final
+#define HASH_INIT                       SHA1_Init
+#define HASH_BLOCK_DATA_ORDER           sha1_block_data_order
+#define Xupdate(a,ix,ia,ib,ic,id)       ( (a)=(ia^ib^ic^id),    \
+                                          ix=(a)=ROTATE((a),1)  \
+                                        )
+
+static void sha1_block_data_order(SHA_CTX *c, const void *p, size_t num);
+
+#include "md32_common.h"
+
+#define INIT_DATA_h0 0x67452301UL
+#define INIT_DATA_h1 0xefcdab89UL
+#define INIT_DATA_h2 0x98badcfeUL
+#define INIT_DATA_h3 0x10325476UL
+#define INIT_DATA_h4 0xc3d2e1f0UL
+
+static int HASH_INIT(SHA_CTX *c)
+{
+    memset(c, 0, sizeof(*c));
+    c->h0 = INIT_DATA_h0;
+    c->h1 = INIT_DATA_h1;
+    c->h2 = INIT_DATA_h2;
+    c->h3 = INIT_DATA_h3;
+    c->h4 = INIT_DATA_h4;
+    return 1;
+}
+
+#define K_00_19 0x5a827999UL
+#define K_20_39 0x6ed9eba1UL
+#define K_40_59 0x8f1bbcdcUL
+#define K_60_79 0xca62c1d6UL
+
+/*
+ * As pointed out by Wei Dai, F() below can be simplified to the code in
+ * F_00_19.  Wei attributes these optimizations to Peter Gutmann's SHS code,
+ * and he attributes it to Rich Schroeppel.
+ *      #define F(x,y,z) (((x) & (y)) | ((~(x)) & (z)))
+ * I've just become aware of another tweak to be made, again from Wei Dai,
+ * in F_40_59, (x&a)|(y&a) -> (x|y)&a
+ */
+#define F_00_19(b,c,d)  ((((c) ^ (d)) & (b)) ^ (d))
+#define F_20_39(b,c,d)  ((b) ^ (c) ^ (d))
+#define F_40_59(b,c,d)  (((b) & (c)) | (((b)|(c)) & (d)))
+#define F_60_79(b,c,d)  F_20_39(b,c,d)
+
+#ifndef OPENSSL_SMALL_FOOTPRINT
+
+# define BODY_00_15(i,a,b,c,d,e,f,xi) \
+        (f)=xi+(e)+K_00_19+ROTATE((a),5)+F_00_19((b),(c),(d)); \
+        (b)=ROTATE((b),30);
+
+# define BODY_16_19(i,a,b,c,d,e,f,xi,xa,xb,xc,xd) \
+        Xupdate(f,xi,xa,xb,xc,xd); \
+        (f)+=(e)+K_00_19+ROTATE((a),5)+F_00_19((b),(c),(d)); \
+        (b)=ROTATE((b),30);
+
+# define BODY_20_31(i,a,b,c,d,e,f,xi,xa,xb,xc,xd) \
+        Xupdate(f,xi,xa,xb,xc,xd); \
+        (f)+=(e)+K_20_39+ROTATE((a),5)+F_20_39((b),(c),(d)); \
+        (b)=ROTATE((b),30);
+
+# define BODY_32_39(i,a,b,c,d,e,f,xa,xb,xc,xd) \
+        Xupdate(f,xa,xa,xb,xc,xd); \
+        (f)+=(e)+K_20_39+ROTATE((a),5)+F_20_39((b),(c),(d)); \
+        (b)=ROTATE((b),30);
+
+# define BODY_40_59(i,a,b,c,d,e,f,xa,xb,xc,xd) \
+        Xupdate(f,xa,xa,xb,xc,xd); \
+        (f)+=(e)+K_40_59+ROTATE((a),5)+F_40_59((b),(c),(d)); \
+        (b)=ROTATE((b),30);
+
+# define BODY_60_79(i,a,b,c,d,e,f,xa,xb,xc,xd) \
+        Xupdate(f,xa,xa,xb,xc,xd); \
+        (f)=xa+(e)+K_60_79+ROTATE((a),5)+F_60_79((b),(c),(d)); \
+        (b)=ROTATE((b),30);
+
+# ifdef X
+#  undef X
+# endif
+  /*
+   * Originally X was an array. As it's automatic it's natural
+   * to expect RISC compiler to accommodate at least part of it in
+   * the register bank, isn't it? Unfortunately not all compilers
+   * "find" this expectation reasonable:-( On order to make such
+   * compilers generate better code I replace X[] with a bunch of
+   * X0, X1, etc. See the function body below...
+   */
+#  define X(i)   XX##i
+
+static void HASH_BLOCK_DATA_ORDER(SHA_CTX *c, const void *p, size_t num)
+{
+    const unsigned char *data = p;
+    register unsigned MD32_REG_T A, B, C, D, E, T, l;
+    unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7,
+        XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15;
+
+    A = c->h0;
+    B = c->h1;
+    C = c->h2;
+    D = c->h3;
+    E = c->h4;
+
+    for (;;) {
+        const union {
+            long one;
+            char little;
+        } is_endian = {
+            1
+        };
+
+        if (!is_endian.little && sizeof(SHA_LONG) == 4
+            && ((size_t)p % 4) == 0) {
+            const SHA_LONG *W = (const SHA_LONG *)data;
+
+            X(0) = W[0];
+            X(1) = W[1];
+            BODY_00_15(0, A, B, C, D, E, T, X(0));
+            X(2) = W[2];
+            BODY_00_15(1, T, A, B, C, D, E, X(1));
+            X(3) = W[3];
+            BODY_00_15(2, E, T, A, B, C, D, X(2));
+            X(4) = W[4];
+            BODY_00_15(3, D, E, T, A, B, C, X(3));
+            X(5) = W[5];
+            BODY_00_15(4, C, D, E, T, A, B, X(4));
+            X(6) = W[6];
+            BODY_00_15(5, B, C, D, E, T, A, X(5));
+            X(7) = W[7];
+            BODY_00_15(6, A, B, C, D, E, T, X(6));
+            X(8) = W[8];
+            BODY_00_15(7, T, A, B, C, D, E, X(7));
+            X(9) = W[9];
+            BODY_00_15(8, E, T, A, B, C, D, X(8));
+            X(10) = W[10];
+            BODY_00_15(9, D, E, T, A, B, C, X(9));
+            X(11) = W[11];
+            BODY_00_15(10, C, D, E, T, A, B, X(10));
+            X(12) = W[12];
+            BODY_00_15(11, B, C, D, E, T, A, X(11));
+            X(13) = W[13];
+            BODY_00_15(12, A, B, C, D, E, T, X(12));
+            X(14) = W[14];
+            BODY_00_15(13, T, A, B, C, D, E, X(13));
+            X(15) = W[15];
+            BODY_00_15(14, E, T, A, B, C, D, X(14));
+            BODY_00_15(15, D, E, T, A, B, C, X(15));
+
+            data += SHA_CBLOCK;
+        } else {
+            (void)HOST_c2l(data, l);
+            X(0) = l;
+            (void)HOST_c2l(data, l);
+            X(1) = l;
+            BODY_00_15(0, A, B, C, D, E, T, X(0));
+            (void)HOST_c2l(data, l);
+            X(2) = l;
+            BODY_00_15(1, T, A, B, C, D, E, X(1));
+            (void)HOST_c2l(data, l);
+            X(3) = l;
+            BODY_00_15(2, E, T, A, B, C, D, X(2));
+            (void)HOST_c2l(data, l);
+            X(4) = l;
+            BODY_00_15(3, D, E, T, A, B, C, X(3));
+            (void)HOST_c2l(data, l);
+            X(5) = l;
+            BODY_00_15(4, C, D, E, T, A, B, X(4));
+            (void)HOST_c2l(data, l);
+            X(6) = l;
+            BODY_00_15(5, B, C, D, E, T, A, X(5));
+            (void)HOST_c2l(data, l);
+            X(7) = l;
+            BODY_00_15(6, A, B, C, D, E, T, X(6));
+            (void)HOST_c2l(data, l);
+            X(8) = l;
+            BODY_00_15(7, T, A, B, C, D, E, X(7));
+            (void)HOST_c2l(data, l);
+            X(9) = l;
+            BODY_00_15(8, E, T, A, B, C, D, X(8));
+            (void)HOST_c2l(data, l);
+            X(10) = l;
+            BODY_00_15(9, D, E, T, A, B, C, X(9));
+            (void)HOST_c2l(data, l);
+            X(11) = l;
+            BODY_00_15(10, C, D, E, T, A, B, X(10));
+            (void)HOST_c2l(data, l);
+            X(12) = l;
+            BODY_00_15(11, B, C, D, E, T, A, X(11));
+            (void)HOST_c2l(data, l);
+            X(13) = l;
+            BODY_00_15(12, A, B, C, D, E, T, X(12));
+            (void)HOST_c2l(data, l);
+            X(14) = l;
+            BODY_00_15(13, T, A, B, C, D, E, X(13));
+            (void)HOST_c2l(data, l);
+            X(15) = l;
+            BODY_00_15(14, E, T, A, B, C, D, X(14));
+            BODY_00_15(15, D, E, T, A, B, C, X(15));
+        }
+
+        BODY_16_19(16, C, D, E, T, A, B, X(0), X(0), X(2), X(8), X(13));
+        BODY_16_19(17, B, C, D, E, T, A, X(1), X(1), X(3), X(9), X(14));
+        BODY_16_19(18, A, B, C, D, E, T, X(2), X(2), X(4), X(10), X(15));
+        BODY_16_19(19, T, A, B, C, D, E, X(3), X(3), X(5), X(11), X(0));
+
+        BODY_20_31(20, E, T, A, B, C, D, X(4), X(4), X(6), X(12), X(1));
+        BODY_20_31(21, D, E, T, A, B, C, X(5), X(5), X(7), X(13), X(2));
+        BODY_20_31(22, C, D, E, T, A, B, X(6), X(6), X(8), X(14), X(3));
+        BODY_20_31(23, B, C, D, E, T, A, X(7), X(7), X(9), X(15), X(4));
+        BODY_20_31(24, A, B, C, D, E, T, X(8), X(8), X(10), X(0), X(5));
+        BODY_20_31(25, T, A, B, C, D, E, X(9), X(9), X(11), X(1), X(6));
+        BODY_20_31(26, E, T, A, B, C, D, X(10), X(10), X(12), X(2), X(7));
+        BODY_20_31(27, D, E, T, A, B, C, X(11), X(11), X(13), X(3), X(8));
+        BODY_20_31(28, C, D, E, T, A, B, X(12), X(12), X(14), X(4), X(9));
+        BODY_20_31(29, B, C, D, E, T, A, X(13), X(13), X(15), X(5), X(10));
+        BODY_20_31(30, A, B, C, D, E, T, X(14), X(14), X(0), X(6), X(11));
+        BODY_20_31(31, T, A, B, C, D, E, X(15), X(15), X(1), X(7), X(12));
+
+        BODY_32_39(32, E, T, A, B, C, D, X(0), X(2), X(8), X(13));
+        BODY_32_39(33, D, E, T, A, B, C, X(1), X(3), X(9), X(14));
+        BODY_32_39(34, C, D, E, T, A, B, X(2), X(4), X(10), X(15));
+        BODY_32_39(35, B, C, D, E, T, A, X(3), X(5), X(11), X(0));
+        BODY_32_39(36, A, B, C, D, E, T, X(4), X(6), X(12), X(1));
+        BODY_32_39(37, T, A, B, C, D, E, X(5), X(7), X(13), X(2));
+        BODY_32_39(38, E, T, A, B, C, D, X(6), X(8), X(14), X(3));
+        BODY_32_39(39, D, E, T, A, B, C, X(7), X(9), X(15), X(4));
+
+        BODY_40_59(40, C, D, E, T, A, B, X(8), X(10), X(0), X(5));
+        BODY_40_59(41, B, C, D, E, T, A, X(9), X(11), X(1), X(6));
+        BODY_40_59(42, A, B, C, D, E, T, X(10), X(12), X(2), X(7));
+        BODY_40_59(43, T, A, B, C, D, E, X(11), X(13), X(3), X(8));
+        BODY_40_59(44, E, T, A, B, C, D, X(12), X(14), X(4), X(9));
+        BODY_40_59(45, D, E, T, A, B, C, X(13), X(15), X(5), X(10));
+        BODY_40_59(46, C, D, E, T, A, B, X(14), X(0), X(6), X(11));
+        BODY_40_59(47, B, C, D, E, T, A, X(15), X(1), X(7), X(12));
+        BODY_40_59(48, A, B, C, D, E, T, X(0), X(2), X(8), X(13));
+        BODY_40_59(49, T, A, B, C, D, E, X(1), X(3), X(9), X(14));
+        BODY_40_59(50, E, T, A, B, C, D, X(2), X(4), X(10), X(15));
+        BODY_40_59(51, D, E, T, A, B, C, X(3), X(5), X(11), X(0));
+        BODY_40_59(52, C, D, E, T, A, B, X(4), X(6), X(12), X(1));
+        BODY_40_59(53, B, C, D, E, T, A, X(5), X(7), X(13), X(2));
+        BODY_40_59(54, A, B, C, D, E, T, X(6), X(8), X(14), X(3));
+        BODY_40_59(55, T, A, B, C, D, E, X(7), X(9), X(15), X(4));
+        BODY_40_59(56, E, T, A, B, C, D, X(8), X(10), X(0), X(5));
+        BODY_40_59(57, D, E, T, A, B, C, X(9), X(11), X(1), X(6));
+        BODY_40_59(58, C, D, E, T, A, B, X(10), X(12), X(2), X(7));
+        BODY_40_59(59, B, C, D, E, T, A, X(11), X(13), X(3), X(8));
+
+        BODY_60_79(60, A, B, C, D, E, T, X(12), X(14), X(4), X(9));
+        BODY_60_79(61, T, A, B, C, D, E, X(13), X(15), X(5), X(10));
+        BODY_60_79(62, E, T, A, B, C, D, X(14), X(0), X(6), X(11));
+        BODY_60_79(63, D, E, T, A, B, C, X(15), X(1), X(7), X(12));
+        BODY_60_79(64, C, D, E, T, A, B, X(0), X(2), X(8), X(13));
+        BODY_60_79(65, B, C, D, E, T, A, X(1), X(3), X(9), X(14));
+        BODY_60_79(66, A, B, C, D, E, T, X(2), X(4), X(10), X(15));
+        BODY_60_79(67, T, A, B, C, D, E, X(3), X(5), X(11), X(0));
+        BODY_60_79(68, E, T, A, B, C, D, X(4), X(6), X(12), X(1));
+        BODY_60_79(69, D, E, T, A, B, C, X(5), X(7), X(13), X(2));
+        BODY_60_79(70, C, D, E, T, A, B, X(6), X(8), X(14), X(3));
+        BODY_60_79(71, B, C, D, E, T, A, X(7), X(9), X(15), X(4));
+        BODY_60_79(72, A, B, C, D, E, T, X(8), X(10), X(0), X(5));
+        BODY_60_79(73, T, A, B, C, D, E, X(9), X(11), X(1), X(6));
+        BODY_60_79(74, E, T, A, B, C, D, X(10), X(12), X(2), X(7));
+        BODY_60_79(75, D, E, T, A, B, C, X(11), X(13), X(3), X(8));
+        BODY_60_79(76, C, D, E, T, A, B, X(12), X(14), X(4), X(9));
+        BODY_60_79(77, B, C, D, E, T, A, X(13), X(15), X(5), X(10));
+        BODY_60_79(78, A, B, C, D, E, T, X(14), X(0), X(6), X(11));
+        BODY_60_79(79, T, A, B, C, D, E, X(15), X(1), X(7), X(12));
+
+        c->h0 = (c->h0 + E) & 0xffffffffL;
+        c->h1 = (c->h1 + T) & 0xffffffffL;
+        c->h2 = (c->h2 + A) & 0xffffffffL;
+        c->h3 = (c->h3 + B) & 0xffffffffL;
+        c->h4 = (c->h4 + C) & 0xffffffffL;
+
+        if (--num == 0)
+            break;
+
+        A = c->h0;
+        B = c->h1;
+        C = c->h2;
+        D = c->h3;
+        E = c->h4;
+
+    }
+}
+
+#else                           /* OPENSSL_SMALL_FOOTPRINT */
+
+# define BODY_00_15(xi)           do {   \
+        T=E+K_00_19+F_00_19(B,C,D);     \
+        E=D, D=C, C=ROTATE(B,30), B=A;  \
+        A=ROTATE(A,5)+T+xi;         } while(0)
+
+# define BODY_16_19(xa,xb,xc,xd)  do {   \
+        Xupdate(T,xa,xa,xb,xc,xd);      \
+        T+=E+K_00_19+F_00_19(B,C,D);    \
+        E=D, D=C, C=ROTATE(B,30), B=A;  \
+        A=ROTATE(A,5)+T;            } while(0)
+
+# define BODY_20_39(xa,xb,xc,xd)  do {   \
+        Xupdate(T,xa,xa,xb,xc,xd);      \
+        T+=E+K_20_39+F_20_39(B,C,D);    \
+        E=D, D=C, C=ROTATE(B,30), B=A;  \
+        A=ROTATE(A,5)+T;            } while(0)
+
+# define BODY_40_59(xa,xb,xc,xd)  do {   \
+        Xupdate(T,xa,xa,xb,xc,xd);      \
+        T+=E+K_40_59+F_40_59(B,C,D);    \
+        E=D, D=C, C=ROTATE(B,30), B=A;  \
+        A=ROTATE(A,5)+T;            } while(0)
+
+# define BODY_60_79(xa,xb,xc,xd)  do {   \
+        Xupdate(T,xa,xa,xb,xc,xd);      \
+        T=E+K_60_79+F_60_79(B,C,D);     \
+        E=D, D=C, C=ROTATE(B,30), B=A;  \
+        A=ROTATE(A,5)+T+xa;         } while(0)
+
+static void HASH_BLOCK_DATA_ORDER(SHA_CTX *c, const void *p, size_t num)
+{
+    const unsigned char *data = p;
+    register unsigned MD32_REG_T A, B, C, D, E, T, l;
+    int i;
+    SHA_LONG X[16];
+
+    A = c->h0;
+    B = c->h1;
+    C = c->h2;
+    D = c->h3;
+    E = c->h4;
+
+    for (;;) {
+        for (i = 0; i < 16; i++) {
+            (void)HOST_c2l(data, l);
+            X[i] = l;
+            BODY_00_15(X[i]);
+        }
+        for (i = 0; i < 4; i++) {
+            BODY_16_19(X[i], X[i + 2], X[i + 8], X[(i + 13) & 15]);
+        }
+        for (; i < 24; i++) {
+            BODY_20_39(X[i & 15], X[(i + 2) & 15], X[(i + 8) & 15],
+                       X[(i + 13) & 15]);
+        }
+        for (i = 0; i < 20; i++) {
+            BODY_40_59(X[(i + 8) & 15], X[(i + 10) & 15], X[i & 15],
+                       X[(i + 5) & 15]);
+        }
+        for (i = 4; i < 24; i++) {
+            BODY_60_79(X[(i + 8) & 15], X[(i + 10) & 15], X[i & 15],
+                       X[(i + 5) & 15]);
+        }
+
+        c->h0 = (c->h0 + A) & 0xffffffffL;
+        c->h1 = (c->h1 + B) & 0xffffffffL;
+        c->h2 = (c->h2 + C) & 0xffffffffL;
+        c->h3 = (c->h3 + D) & 0xffffffffL;
+        c->h4 = (c->h4 + E) & 0xffffffffL;
+
+        if (--num == 0)
+            break;
+
+        A = c->h0;
+        B = c->h1;
+        C = c->h2;
+        D = c->h3;
+        E = c->h4;
+
+    }
+}
+
+#endif
diff --git a/main.c b/main.c
index 6a6ac55967ef9..6774c2e6a1007 100644
--- a/main.c
+++ b/main.c
@@ -1597,6 +1597,8 @@ int main(int argc,char *argv[])
 		exit_cleanup(RERR_SYNTAX);
 	}
 
+	mdlib_initialize();
+
 	SIGACTMASK(SIGINT, sig_int);
 	SIGACTMASK(SIGHUP, sig_int);
 	SIGACTMASK(SIGTERM, sig_int);
diff --git a/rsync.h b/rsync.h
index 641ba74073de3..87326e9e284ca 100644
--- a/rsync.h
+++ b/rsync.h
@@ -98,7 +98,7 @@
 			     == ((unsigned)(b2) & (unsigned)(mask)))
 
 /* update this if you make incompatible changes */
-#define PROTOCOL_VERSION 31
+#define PROTOCOL_VERSION 32
 
 /* This is used when working on a new protocol version in CVS, and should
  * be a new non-zero value for each CVS change that affects the protocol.
-- 
2.25.0




More information about the rsync mailing list