Re-visit SHA-1 code
authorEric Biggers <ebiggers3@gmail.com>
Sun, 15 Jun 2014 16:34:57 +0000 (11:34 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Mon, 16 Jun 2014 01:56:54 +0000 (20:56 -0500)
- Fixed build failures when configured with --enable-ssse3-sha1.

- Actually calculate the message digest correctly in the SSSE3-optimized
  version!  The Intel code just does block transformations, not arbitrary
  updates; the previous code did not reflect this.

- Use an appropriate fallback when the CPU does not support SSSE3
  instructions; don't just call abort()!

- Improve sha1_update() and sha1_final().  They should now be slightly
  faster, as well as easier to understand.

- Use beXX_to_cpu() and cpu_to_beXX() macros instead of hard-coding
  endian conversions.

12 files changed:
Makefile.am
NEWS
build-aux/nasm_lt.sh [new file with mode: 0755]
build-aux/strip_fPIC.sh [deleted file]
configure.ac
include/wimlib/endianness.h
include/wimlib/sha1.h
include/wimlib/types.h
m4/ax_prog_nasm.m4 [deleted file]
m4/nasm.m4 [new file with mode: 0644]
src/sha1-ssse3.asm
src/sha1.c

index bbdfd8e..f9754a1 100644 (file)
@@ -146,12 +146,13 @@ endif
 
 EXTRA_libwim_la_SOURCES = src/sha1-ssse3.asm
 libwim_la_DEPENDENCIES = $(SSSE3_SHA1_OBJ)
-STRIP_FPIC = sh $(top_srcdir)/build-aux/strip_fPIC.sh
 
-sha1-ssse3.lo:src/sha1-ssse3.asm
-       $(LIBTOOL) --mode=compile --tag=CC $(STRIP_FPIC) $(NASM) -f elf64 \
-       -DINTEL_SHA1_UPDATE_DEFAULT_DISPATCH=ssse3_not_found \
-       $<
+src/sha1-ssse3.lo:src/sha1-ssse3.asm
+       $(LIBTOOL) --mode=compile --tag NASM $(srcdir)/build-aux/nasm_lt.sh \
+       $(NASM) $(NAFLAGS) $(NASM_WINDOWS_FLAGS)                            \
+       -DINTEL_SHA1_UPDATE_FUNCNAME=$(NASM_SYMBOL_PREFIX)sha1_transform_blocks_ssse3       \
+       -DINTEL_SHA1_UPDATE_DEFAULT_DISPATCH=$(NASM_SYMBOL_PREFIX)sha1_transform_blocks_default  \
+       $< -o $@
 
 libwim_la_LIBADD =             \
        $(LIBXML2_LIBS)         \
@@ -236,7 +237,7 @@ dist_bin_SCRIPTS = programs/mkwinpeimg
 include_HEADERS = include/wimlib.h
 
 EXTRA_DIST =                                   \
-       build-aux/strip_fPIC.sh                 \
+       build-aux/nasm_lt.sh                    \
        archlinux                               \
        debian                                  \
        rpm                                     \
diff --git a/NEWS b/NEWS
index 2ddde0e..99b6a7b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -51,6 +51,8 @@ Version 1.7.0-BETA:
        support.  Use --without-fuse to disable support for mounting WIM images;
        this will also disable the need for extended attribute support.
 
+       Configuring with --enable-ssse3-sha1 now works correctly.
+
        The shared library version has been bumped up.  The main
        incompatibilities are:
 
diff --git a/build-aux/nasm_lt.sh b/build-aux/nasm_lt.sh
new file mode 100755 (executable)
index 0000000..6cd7329
--- /dev/null
@@ -0,0 +1,57 @@
+#! /bin/sh
+command=""
+infile=""
+o_opt=no
+pic=no
+while [ $# -gt 0 ]; do
+    case "$1" in
+        -DPIC|-fPIC|-fpic|-Kpic|-KPIC)
+            if [ "$pic" != "yes" ] ; then
+                command="$command -DPIC"
+                pic=yes
+            fi
+            ;;
+        -f|-fbin|-faout|-faoutb|-fcoff|-felf|-felf64|-fas86| \
+        -fobj|-fwin32|-fwin64|-frdf|-fieee|-fmacho|-fmacho64)
+            # it's a file format specifier for nasm.
+            command="$command $1"
+            ;;
+        -f*)
+            # maybe a code-generation flag for gcc.
+            ;;
+        -[Ii]*)
+            incdir=`echo "$1" | sed 's/^-[Ii]//'`
+            if [ "x$incdir" = x -a "x$2" != x ] ; then
+                case "$2" in
+                    -*) ;;
+                    *) incdir="$2"; shift;;
+                esac
+            fi
+            if [ "x$incdir" != x ] ; then
+                # In the case of NASM, the trailing slash is necessary.
+                incdir=`echo "$incdir" | sed 's%/*$%/%'`
+                command="$command -I$incdir"
+            fi
+            ;;
+        -o*)
+            o_opt=yes
+            command="$command $1"
+            ;;
+        *.asm)
+            infile=$1
+            command="$command $1"
+            ;;
+        *)
+            command="$command $1"
+            ;;
+    esac
+    shift
+done
+if [ "$o_opt" != yes ] ; then
+    # By default, NASM creates an output file
+    # in the same directory as the input file.
+    outfile="-o `echo $infile | sed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.o"
+    command="$command $outfile"
+fi
+echo $command
+exec $command
diff --git a/build-aux/strip_fPIC.sh b/build-aux/strip_fPIC.sh
deleted file mode 100755 (executable)
index 45d34ba..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/sh
-#
-# libtool assumes that the compiler can handle the -fPIC flag
-# This isn't always true (for example, nasm can't handle it)
-command=""
-while [ $# -gt 0 ]; do
-    case "$1" in
-        -fPIC)
-            # Ignore -fPIC option
-            ;;
-        -fno-common)
-            # Ignore -fPIC and -DPIC options
-            ;;
-        *)
-            command="$command $1"
-            ;;
-    esac
-    shift
-done
-echo $command
-exec $command
index 7698dc6..1e546ff 100644 (file)
@@ -179,8 +179,8 @@ WINDOWS_CPPFLAGS=""
 WINDOWS_LDFLAGS=""
 WINDOWS_LDADD=""
 
-case "$host" in
-       *-*-mingw*)
+case "$host_os" in
+       mingw*)
                # Native Windows
                WITH_NTFS_3G_DEFAULT="no"
                WITH_FUSE_DEFAULT="no"
@@ -191,14 +191,6 @@ case "$host" in
                WINDOWS_LDFLAGS="-no-undefined"
                WINDOWS_LDADD="-lshlwapi"
                ;;
-       *-*-cygwin*)
-               # Cygwin (WARNING: not well supported)
-               AC_MSG_WARN([wimlib has not been tested with Cygwin!  Please do
-                            a Windows-native build with MinGW-w64 instead])
-               WITH_NTFS_3G_DEFAULT="no"
-               WITH_FUSE_DEFAULT="no"
-               VISIBILITY_CFLAGS=""
-               ;;
        *)
                # UNIX / other
                ;;
@@ -303,9 +295,20 @@ AC_MSG_RESULT([$ENABLE_SSSE3_SHA1])
 if test "x$ENABLE_SSSE3_SHA1" = "xyes"; then
        AC_DEFINE([ENABLE_SSSE3_SHA1], [1],
                        [Define to 1 if using vectorized implementation of SHA1])
-       SSSE3_SHA1_OBJ=sha1-ssse3.lo
-       AX_PROG_NASM
-       AC_SUBST([NASM], [$nasm])
+       SSSE3_SHA1_OBJ=src/sha1-ssse3.lo
+       AC_PROG_NASM
+       NASM_SYMBOL_PREFIX=""
+       NASM_WINDOWS_FLAGS=
+       if test "x$WINDOWS_NATIVE_BUILD" = "xyes"; then
+               NASM_WINDOWS_FLAGS="-DWIN_ABI"
+       fi
+       case "$host_os" in
+               darwin* | rhapsody* | nextstep* | openstep* | macos*)
+                       NASM_SYMBOL_PREFIX="_"
+                       ;;
+       esac
+       AC_SUBST([NASM_WINDOWS_FLAGS], $NASM_WINDOWS_FLAGS)
+       AC_SUBST([NASM_SYMBOL_PREFIX], $NASM_SYMBOL_PREFIX)
 else
        SSSE3_SHA1_OBJ=
 fi
index 3cefe68..2572616 100644 (file)
@@ -44,6 +44,13 @@ bswap64(u64 n)
 #    define cpu_to_le16(n) bswap16(n)
 #    define cpu_to_le32(n) bswap32(n)
 #    define cpu_to_le64(n) bswap64(n)
+
+#    define cpu_to_be16(n) (n)
+#    define cpu_to_be32(n) (n)
+#    define cpu_to_be64(n) (n)
+#    define be16_to_cpu(n) (n)
+#    define be32_to_cpu(n) (n)
+#    define be64_to_cpu(n) (n)
 #  else
 #    define cpu_to_le16(n) (n)
 #    define cpu_to_le32(n) (n)
@@ -51,6 +58,13 @@ bswap64(u64 n)
 #    define le16_to_cpu(n) (n)
 #    define le32_to_cpu(n) (n)
 #    define le64_to_cpu(n) (n)
+
+#    define be16_to_cpu(n) bswap16(n)
+#    define be32_to_cpu(n) bswap32(n)
+#    define be64_to_cpu(n) bswap64(n)
+#    define cpu_to_be16(n) bswap16(n)
+#    define cpu_to_be32(n) bswap32(n)
+#    define cpu_to_be64(n) bswap64(n)
 #  endif
 #endif /* _NTFS_ENDIANS_H */
 
index 4a19a93..933444a 100644 (file)
@@ -55,26 +55,25 @@ zero_out_hash(u8 hash[SHA1_HASH_SIZE])
 #ifdef WITH_LIBCRYPTO
 
 #include <openssl/sha.h>
+
+#define sha1_init     SHA1_Init
+#define sha1_update   SHA1_Update
+#define sha1_final    SHA1_Final
+
 static inline void
 sha1_buffer(const void *buffer, size_t len, u8 hash[SHA1_HASH_SIZE])
 {
        SHA1(buffer, len, hash);
 }
-#define sha1_init     SHA1_Init
-#define sha1_update   SHA1_Update
-#define sha1_final    SHA1_Final
 
 #else /* WITH_LIBCRYPTO */
 
 typedef struct {
+       u64 bytecount;
        u32 state[5];
-       u32 count[2];
        u8 buffer[64];
 } SHA_CTX;
 
-extern void
-sha1_buffer(const void *buffer, size_t len, u8 hash[SHA1_HASH_SIZE]);
-
 extern void
 sha1_init(SHA_CTX *ctx);
 
@@ -84,6 +83,9 @@ sha1_update(SHA_CTX *ctx, const void *data, size_t len);
 extern void
 sha1_final(u8 hash[SHA1_HASH_SIZE], SHA_CTX *ctx);
 
+extern void
+sha1_buffer(const void *buffer, size_t len, u8 hash[SHA1_HASH_SIZE]);
+
 #endif /* !WITH_LIBCRYPTO */
 
 #endif /* _WIMLIB_SHA1_H */
index dc0b550..65c1418 100644 (file)
@@ -32,6 +32,13 @@ typedef uint8_t  sle8;
 typedef uint16_t sle16;
 typedef uint32_t sle32;
 typedef uint64_t sle64;
+
+/* Unsigned big endian types of exact size */
+typedef uint8_t  be8;
+typedef uint16_t be16;
+typedef uint32_t be32;
+typedef uint64_t be64;
+
 #endif
 
 /* A pointer to 'utf16lechar' indicates a UTF-16LE encoded string */
diff --git a/m4/ax_prog_nasm.m4 b/m4/ax_prog_nasm.m4
deleted file mode 100644 (file)
index 50b01f7..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-# ===========================================================================
-#       http://www.gnu.org/software/autoconf-archive/ax_prog_nasm.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_PROG_NASM([ACTION-IF-NOT-FOUND])
-#
-# DESCRIPTION
-#
-#   This macro searches for the NASM assembler and sets the variable "nasm"
-#   to the name of the application or to "no" if not found. If
-#   ACTION-IF-NOT-FOUND is not specified, configure will fail when the
-#   program is not found.
-#
-#   Example:
-#
-#     AX_PROG_NASM()
-#     AX_PROG_NASM([nasm_avail="no"])
-#
-# LICENSE
-#
-#   Copyright (c) 2007,2009 Bogdan Drozdowski <bogdandr@op.pl>
-#
-#   This program is free software: you can redistribute it and/or modify it
-#   under the terms of the GNU Lesser General Public License as published by
-#   the Free Software Foundation, either version 3 of the License, or (at
-#   your option) any later version.
-#
-#   This program is distributed in the hope that it will be useful, but
-#   WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
-#   General Public License for more details.
-#
-#   You should have received a copy of the GNU Lesser General Public License
-#   along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#   As a special exception, the respective Autoconf Macro's copyright owner
-#   gives unlimited permission to copy, distribute and modify the configure
-#   scripts that are the output of Autoconf when processing the Macro. You
-#   need not follow the terms of the GNU General Public License when using
-#   or distributing such scripts, even though portions of the text of the
-#   Macro appear in them. The GNU General Public License (GPL) does govern
-#   all other use of the material that constitutes the Autoconf Macro.
-#
-#   This special exception to the GPL applies to versions of the Autoconf
-#   Macro released by the Autoconf Archive. When you make and distribute a
-#   modified version of the Autoconf Macro, you may extend this special
-#   exception to the GPL to apply to your modified version as well.
-
-#serial 9
-
-AC_DEFUN([AX_PROG_NASM],[
-AC_CHECK_PROGS(nasm,[nasm nasmw],no)
-if test "x$nasm" = "xno" ;
-then
-       ifelse($#,0,[AC_MSG_ERROR([NASM assembler not found])],
-        $1)
-fi
-])
diff --git a/m4/nasm.m4 b/m4/nasm.m4
new file mode 100644 (file)
index 0000000..978a469
--- /dev/null
@@ -0,0 +1,212 @@
+# AC_PROG_NASM
+# --------------------------
+# Check that NASM exists and determine flags
+AC_DEFUN([AC_PROG_NASM],[
+
+AC_CHECK_PROGS(NASM, [nasm nasmw yasm])
+test -z "$NASM" && AC_MSG_ERROR([no nasm (Netwide Assembler) found])
+
+AC_MSG_CHECKING([for object file format of host system])
+case "$host_os" in
+  cygwin* | mingw* | pw32* | interix*)
+    case "$host_cpu" in
+      x86_64)
+        objfmt='Win64-COFF'
+        ;;
+      *)
+        objfmt='Win32-COFF'
+        ;;
+    esac
+  ;;
+  msdosdjgpp* | go32*)
+    objfmt='COFF'
+  ;;
+  os2-emx*)                    # not tested
+    objfmt='MSOMF'             # obj
+  ;;
+  linux*coff* | linux*oldld*)
+    objfmt='COFF'              # ???
+  ;;
+  linux*aout*)
+    objfmt='a.out'
+  ;;
+  linux*)
+    case "$host_cpu" in
+      x86_64)
+        objfmt='ELF64'
+        ;;
+      *)
+        objfmt='ELF'
+        ;;
+    esac
+  ;;
+  kfreebsd* | freebsd* | netbsd* | openbsd*)
+    if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+      objfmt='BSD-a.out'
+    else
+      case "$host_cpu" in
+        x86_64 | amd64)
+          objfmt='ELF64'
+          ;;
+        *)
+          objfmt='ELF'
+          ;;
+      esac
+    fi
+  ;;
+  solaris* | sunos* | sysv* | sco*)
+    case "$host_cpu" in
+      x86_64)
+        objfmt='ELF64'
+        ;;
+      *)
+        objfmt='ELF'
+        ;;
+    esac
+  ;;
+  darwin* | rhapsody* | nextstep* | openstep* | macos*)
+    case "$host_cpu" in
+      x86_64)
+        objfmt='Mach-O64'
+        ;;
+      *)
+        objfmt='Mach-O'
+        ;;
+    esac
+  ;;
+  *)
+    objfmt='ELF ?'
+  ;;
+esac
+
+AC_MSG_RESULT([$objfmt])
+if test "$objfmt" = 'ELF ?'; then
+  objfmt='ELF'
+  AC_MSG_WARN([unexpected host system. assumed that the format is $objfmt.])
+fi
+
+AC_MSG_CHECKING([for object file format specifier (NAFLAGS) ])
+case "$objfmt" in
+  MSOMF)      NAFLAGS='-fobj -DOBJ32';;
+  Win32-COFF) NAFLAGS='-fwin32 -DWIN32';;
+  Win64-COFF) NAFLAGS='-fwin64 -DWIN64 -D__x86_64__';;
+  COFF)       NAFLAGS='-fcoff -DCOFF';;
+  a.out)      NAFLAGS='-faout -DAOUT';;
+  BSD-a.out)  NAFLAGS='-faoutb -DAOUT';;
+  ELF)        NAFLAGS='-felf -DELF';;
+  ELF64)      NAFLAGS='-felf64 -DELF -D__x86_64__';;
+  RDF)        NAFLAGS='-frdf -DRDF';;
+  Mach-O)     NAFLAGS='-fmacho -DMACHO';;
+  Mach-O64)   NAFLAGS='-fmacho64 -DMACHO -D__x86_64__';;
+esac
+AC_MSG_RESULT([$NAFLAGS])
+AC_SUBST([NAFLAGS])
+
+AC_MSG_CHECKING([whether the assembler ($NASM $NAFLAGS) works])
+cat > conftest.asm <<EOF
+[%line __oline__ "configure"
+        section .text
+        global  _main,main
+_main:
+main:   xor     eax,eax
+        ret
+]EOF
+try_nasm='$NASM $NAFLAGS -o conftest.o conftest.asm'
+if AC_TRY_EVAL(try_nasm) && test -s conftest.o; then
+  AC_MSG_RESULT(yes)
+else
+  echo "configure: failed program was:" >&AC_FD_CC
+  cat conftest.asm >&AC_FD_CC
+  rm -rf conftest*
+  AC_MSG_RESULT(no)
+  AC_MSG_ERROR([installation or configuration problem: assembler cannot create object files.])
+fi
+
+AC_MSG_CHECKING([whether the linker accepts assembler output])
+try_nasm='${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.o $LIBS 1>&AC_FD_CC'
+if AC_TRY_EVAL(try_nasm) && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  AC_MSG_RESULT(yes)
+else
+  rm -rf conftest*
+  AC_MSG_RESULT(no)
+  AC_MSG_ERROR([configuration problem: maybe object file format mismatch.])
+fi
+
+])
+
+# AC_CHECK_COMPATIBLE_ARM_ASSEMBLER_IFELSE
+# --------------------------
+# Test whether the assembler is suitable and supports NEON instructions
+AC_DEFUN([AC_CHECK_COMPATIBLE_ARM_ASSEMBLER_IFELSE],[
+  ac_good_gnu_arm_assembler=no
+  ac_save_CC="$CC"
+  ac_save_CFLAGS="$CFLAGS"
+  CFLAGS="$CCASFLAGS -x assembler-with-cpp"
+  CC="$CCAS"
+  AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+    .text
+    .fpu neon
+    .arch armv7a
+    .object_arch armv4
+    .arm
+    pld [r0]
+    vmovn.u16 d0, q0]])], ac_good_gnu_arm_assembler=yes)
+
+  ac_use_gas_preprocessor=no
+  if test "x$ac_good_gnu_arm_assembler" = "xno" ; then
+    CC="gas-preprocessor.pl $CCAS"
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+      .text
+      .fpu neon
+      .arch armv7a
+      .object_arch armv4
+      .arm
+      pld [r0]
+      vmovn.u16 d0, q0]])], ac_use_gas_preprocessor=yes)
+  fi
+  CFLAGS="$ac_save_CFLAGS"
+  CC="$ac_save_CC"
+
+  if test "x$ac_use_gas_preprocessor" = "xyes" ; then
+    CCAS="gas-preprocessor.pl $CCAS"
+    AC_SUBST([CCAS])
+    ac_good_gnu_arm_assembler=yes
+  fi
+
+  if test "x$ac_good_gnu_arm_assembler" = "xyes" ; then
+    $1
+  else
+    $2
+  fi
+])
+
+# AC_CHECK_COMPATIBLE_MIPSEL_ASSEMBLER_IFELSE
+# --------------------------
+# Test whether the assembler is suitable and supports MIPS instructions
+AC_DEFUN([AC_CHECK_COMPATIBLE_MIPSEL_ASSEMBLER_IFELSE],[
+  have_mips_dspr2=no
+  ac_save_CFLAGS="$CFLAGS"
+  CFLAGS="$CCASFLAGS -mdspr2"
+
+  AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+
+  int main ()
+  {
+    int c = 0, a = 0, b = 0;
+    __asm__ __volatile__ (
+        "precr.qb.ph %[c], %[a], %[b]          \n\t"
+        : [c] "=r" (c)
+        : [a] "r" (a), [b] "r" (b)
+    );
+    return c;
+  }
+  ]])], have_mips_dspr2=yes)
+  CFLAGS=$ac_save_CFLAGS
+
+  if test "x$have_mips_dspr2" = "xyes" ; then
+    $1
+  else
+    $2
+  fi
+])
index f8df3e3..45f7062 100644 (file)
@@ -566,4 +566,4 @@ ud2     ;; in the case no default SHA-1 was provided non-SSSE3 CPUs safely fail
 ret
 
 ; END
-;----------------------
\ No newline at end of file
+;----------------------
index f989005..bd83a41 100644 (file)
 /*
  * sha1.c
  *
- * Parts of this file are based on public domain code written by Steve Reid.
- */
-
-/*
- * Copyright (C) 2012, 2013 Eric Biggers
+ * Implementation of the Secure Hash Algorithm version 1 (FIPS 180-1).
  *
- * This file is part of wimlib, a library for working with WIM files.
+ * Author:  Eric Biggers
+ * Year:    2014
  *
- * wimlib is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
+ * The default SHA-1 transform is based on public domain code by Steve Reid.
  *
- * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ * The author dedicates this file to the public domain.
+ * You can do whatever you want with this file.
  */
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
 
+#include "wimlib/endianness.h"
 #include "wimlib/sha1.h"
 
-#include <string.h>
+/* Dummy SHA-1 message digest of all 0's.  This is used in the WIM format to
+ * mean "SHA-1 not specified".  */
+const u8 zero_hash[20];
 
-/* The SHA1 support in wimlib can use an external libcrypto (part of openssl) or
- * use a built-in SHA1 function.  The built-in functions are either based on
- * Steve Reid's public domain code, or based on Intel's SSSE3 SHA1 code.
- */
+/* If we use libcrypto (e.g. OpenSSL) then we get all the SHA-1 functions for
+ * free.  Otherwise we need to implement them ourselves.  */
 
-const u8 zero_hash[SHA1_HASH_SIZE] = {
-       0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0,
-};
+#ifndef WITH_LIBCRYPTO
 
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
 
-#ifndef WITH_LIBCRYPTO
+#define blk0(i) (tmp[i] = be32_to_cpu(((const be32 *)block)[i]))
 
-/*  Initialize new context */
-void
-sha1_init(SHA_CTX* context)
-{
-       /* SHA1 initialization constants */
-       context->state[0] = 0x67452301;
-       context->state[1] = 0xEFCDAB89;
-       context->state[2] = 0x98BADCFE;
-       context->state[3] = 0x10325476;
-       context->state[4] = 0xC3D2E1F0;
-       context->count[0] = context->count[1] = 0;
-}
+#define blk(i) (tmp[i & 15] = rol(tmp[(i + 13) & 15] ^ \
+                                 tmp[(i +  8) & 15] ^ \
+                                 tmp[(i +  2) & 15] ^ \
+                                 tmp[(i +  0) & 15], 1))
 
-#ifdef ENABLE_SSSE3_SHA1
-extern void
-sha1_update_intel(int *hash, const void* input, size_t num_blocks);
+#define R0(v, w, x, y, z, i) \
+       z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
+       w = rol(w, 30);
 
-void
-sha1_update(SHA_CTX *context, const void *data, size_t len)
-{
-       sha1_update_intel((int*)&context->state, data, len / 64);
-       size_t j = (context->count[0] >> 3) & 63;
-       if ((context->count[0] += len << 3) < (len << 3))
-               context->count[1]++;
-       context->count[1] += (len >> 29);
-}
-#include <stdlib.h>
-void
-ssse3_not_found()
-{
-       fprintf(stderr,
-"Cannot calculate SHA1 message digest: CPU does not support SSSE3\n"
-"instructions!  Recompile wimlib without the --enable-ssse3-sha1 flag\n"
-"to use wimlib on this CPU.\n");
-       abort();
-}
-#else /* ENABLE_SSSE3_SHA1 */
+#define R1(v, w, x, y, z, i) \
+       z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+       w = rol(w, 30);
 
-#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+#define R2(v, w, x, y, z, i) \
+       z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
+       w = rol(w, 30);
 
-/* blk0() and blk() perform the initial expand. */
-/* I got the idea of expanding during the round function from SSLeay */
-/* FIXME: can we do this in an endian-proof way? */
-#ifdef WORDS_BIGENDIAN
-#define blk0(i) block->l[i]
-#else
-#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
-    |(rol(block->l[i],8)&0x00FF00FF))
-#endif
-#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
-    ^block->l[(i+2)&15]^block->l[i&15],1))
+#define R3(v, w, x, y, z, i) \
+       z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+       w = rol(w, 30);
 
-/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
-#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
-#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
-#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
-#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
-#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+#define R4(v, w, x, y, z, i) \
+       z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
+       w = rol(w, 30);
 
-/* Hash a single 512-bit block. This is the core of the algorithm. */
+/* Hash a single 512-bit block. This is the core of the algorithm.  */
 static void
-sha1_transform(u32 state[5], const u8 buffer[64])
+sha1_transform_default(u32 state[5], const u8 block[64])
 {
        u32 a, b, c, d, e;
-       typedef union {
-               u8 c[64];
-               u32 l[16];
-       } CHAR64LONG16;
-       CHAR64LONG16* block;
-
-       u8 workspace[64];
-       block = (CHAR64LONG16*)workspace;
-       memcpy(block, buffer, 64);
+       u32 tmp[16];
 
-       /* Copy context->state[] to working vars */
+       /* Copy ctx->state[] to working vars */
        a = state[0];
        b = state[1];
        c = state[2];
@@ -157,55 +101,104 @@ sha1_transform(u32 state[5], const u8 buffer[64])
        state[4] += e;
 }
 
+#ifdef ENABLE_SSSE3_SHA1
+extern void
+sha1_transform_blocks_ssse3(u32 state[5], const void *data, size_t num_blocks);
+extern void
+sha1_transform_blocks_default(u32 state[5], const void *data, size_t num_blocks);
+#  define sha1_transform_blocks sha1_transform_blocks_ssse3
+#else
+#  define sha1_transform_blocks sha1_transform_blocks_default
+#endif
+
+#ifndef ENABLE_SSSE3_SHA1
+static
+#endif
 void
-sha1_update(SHA_CTX* context, const void *data, const size_t len)
+sha1_transform_blocks_default(u32 state[5], const void *data, size_t num_blocks)
 {
-       size_t i, j;
-
-       j = (context->count[0] >> 3) & 63;
-       if ((context->count[0] += len << 3) < (len << 3))
-               context->count[1]++;
-       context->count[1] += (len >> 29);
-       if ((j + len) > 63) {
-               i = 64 - j;
-               memcpy(&context->buffer[j], data, i);
-               sha1_transform(context->state, context->buffer);
-               for ( ; i + 63 < len; i += 64)
-                       sha1_transform(context->state, data + i);
-               j = 0;
-       } else  {
-               i = 0;
-       }
-       memcpy(&context->buffer[j], data + i, len - i);
+       do {
+               sha1_transform_default(state, data);
+               data += 64;
+       } while (--num_blocks);
 }
 
-#endif /* !ENABLE_SSSE3_SHA1 */
-
-/* Add padding and return the message digest. */
+/* Initializes the specified SHA-1 context.
+ *
+ * After sha1_init(), call sha1_update() zero or more times to provide the data
+ * to be hashed.  Then call sha1_final() to get the final hash.  */
 void
-sha1_final(u8 md[SHA1_HASH_SIZE], SHA_CTX* context)
+sha1_init(SHA_CTX *ctx)
 {
-       u32 i;
-       u8  finalcount[8];
+       ctx->bytecount = 0;
 
-       for (i = 0; i < 8; i++) {
-               finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
-                                       >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
-       }
-       sha1_update(context, (u8 *)"\200", 1);
-       while ((context->count[0] & 504) != 448) {
-               sha1_update(context, (u8 *)"\0", 1);
+       ctx->state[0] = 0x67452301;
+       ctx->state[1] = 0xEFCDAB89;
+       ctx->state[2] = 0x98BADCFE;
+       ctx->state[3] = 0x10325476;
+       ctx->state[4] = 0xC3D2E1F0;
+}
+
+/* Updates the SHA-1 context with @len bytes of data.  */
+void
+sha1_update(SHA_CTX *ctx, const void *data, size_t len)
+{
+       unsigned buffered = ctx->bytecount & 63;
+
+       ctx->bytecount += len;
+
+       if (buffered) {
+               /* Previous block is unfinished.  */
+               if (len < 64 - buffered) {
+                       memcpy(&ctx->buffer[buffered], data, len);
+                       /* Previous block still unfinished.  */
+                       return;
+               } else {
+                       memcpy(&ctx->buffer[buffered], data, 64 - buffered);
+                       /* Finished the previous block.  */
+                       sha1_transform_blocks(ctx->state, ctx->buffer, 1);
+                       data += 64 - buffered;
+                       len -= 64 - buffered;
+               }
        }
-       sha1_update(context, finalcount, 8);  /* Should cause a sha1_transform() */
-       for (i = 0; i < SHA1_HASH_SIZE; i++) {
-               md[i] = (u8)((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+
+       /* Process blocks directly from the input data.  */
+       if (len / 64) {
+               sha1_transform_blocks(ctx->state, data, len / 64);
+               data += len & ~63;
+               len &= 63;
        }
+
+       /* Copy any remaining bytes to the buffer.  */
+       if (len)
+               memcpy(ctx->buffer, data, len);
 }
 
+/* Pad the message and generate the final SHA-1 message digest.  */
 void
-sha1_buffer(const void *buffer, size_t len, u8 md[SHA1_HASH_SIZE])
+sha1_final(u8 md[20], SHA_CTX *ctx)
+{
+       /* Logically, we must append 1 bit, then a variable number of 0 bits,
+        * then the message length in bits as a big-endian integer, so that the
+        * final length is a multiple of the block size.  */
+       static const u8 padding[64] = {0x80, };
+       be64 finalcount = cpu_to_be64(ctx->bytecount << 3);
+       be32 *out = (be32 *)md;
+
+       sha1_update(ctx, padding, 64 - ((ctx->bytecount + 8) & 63));
+       sha1_update(ctx, &finalcount, 8);
+
+       for (int i = 0; i < 5; i++)
+               out[i] = cpu_to_be32(ctx->state[i]);
+}
+
+/* Calculate the SHA-1 message digest of the specified buffer.
+ * @len is the buffer length in bytes.  */
+void
+sha1_buffer(const void *buffer, size_t len, u8 md[20])
 {
        SHA_CTX ctx;
+
        sha1_init(&ctx);
        sha1_update(&ctx, buffer, len);
        sha1_final(md, &ctx);