From: Eric Biggers Date: Sun, 15 Jun 2014 16:34:57 +0000 (-0500) Subject: Re-visit SHA-1 code X-Git-Tag: v1.7.0~6 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=19ce03addb8071555d951a965fa0bd7e3ae7224b Re-visit SHA-1 code - 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. --- diff --git a/Makefile.am b/Makefile.am index bbdfd8e9..f9754a10 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 2ddde0e6..99b6a7b7 100644 --- 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 index 00000000..6cd73294 --- /dev/null +++ b/build-aux/nasm_lt.sh @@ -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 index 45d34ba4..00000000 --- a/build-aux/strip_fPIC.sh +++ /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 diff --git a/configure.ac b/configure.ac index 7698dc66..1e546ff0 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/include/wimlib/endianness.h b/include/wimlib/endianness.h index 3cefe68e..25726166 100644 --- a/include/wimlib/endianness.h +++ b/include/wimlib/endianness.h @@ -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 */ diff --git a/include/wimlib/sha1.h b/include/wimlib/sha1.h index 4a19a93c..933444ac 100644 --- a/include/wimlib/sha1.h +++ b/include/wimlib/sha1.h @@ -55,26 +55,25 @@ zero_out_hash(u8 hash[SHA1_HASH_SIZE]) #ifdef WITH_LIBCRYPTO #include + +#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 */ diff --git a/include/wimlib/types.h b/include/wimlib/types.h index dc0b5501..65c14189 100644 --- a/include/wimlib/types.h +++ b/include/wimlib/types.h @@ -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 index 50b01f77..00000000 --- a/m4/ax_prog_nasm.m4 +++ /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 -# -# 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 . -# -# 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 index 00000000..978a469b --- /dev/null +++ b/m4/nasm.m4 @@ -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 <&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 +]) diff --git a/src/sha1-ssse3.asm b/src/sha1-ssse3.asm index f8df3e3b..45f7062f 100644 --- a/src/sha1-ssse3.asm +++ b/src/sha1-ssse3.asm @@ -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 +;---------------------- diff --git a/src/sha1.c b/src/sha1.c index f989005a..bd83a413 100644 --- a/src/sha1.c +++ b/src/sha1.c @@ -1,126 +1,70 @@ /* * 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 +/* 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 -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);