diff --git a/.gitignore b/.gitignore index 6416eaa..556cc11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /.vscode/ /demo_sha1 /test_sha1 +bin/ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..52a474f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 3.18) +project("SHA1_tests") + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_FLAGS "-Wall") +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin) + +get_filename_component(PARENT_DIR ../ ABSOLUTE) + + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + add_definitions(-D_WIN32) + add_compile_options("/O2") +else() + add_definitions(-D_LINUX) + if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(WARNING "Assuming system in Linux") + endif() + add_compile_options("-O3") + add_compile_options("-g") +endif() + +#--------------------------------------------------------------------- + +add_library(SHA1_lib INTERFACE) + +target_include_directories( + SHA1_lib + INTERFACE + "${CMAKE_SOURCE_DIR}" +) + +#--------------------------------------------------------------------- + +set(TEST test_sha1) + +add_executable( + "${TEST}" + test_sha1.cpp + test_sha1_file.cpp +) + +target_link_libraries( + "${TEST}" + PRIVATE + SHA1_lib +) + +#--------------------------------------------------------------------- + +set(TEST demo_sha1) + +add_executable( + "${TEST}" + demo_sha1.cpp +) + +target_link_libraries( + "${TEST}" + PRIVATE + SHA1_lib +) + +#--------------------------------------------------------------------- diff --git a/Makefile b/Makefile index 41fc3c9..a5d6704 100644 --- a/Makefile +++ b/Makefile @@ -5,10 +5,10 @@ RM = rm -f all: demo_sha1 test_sha1 demo_sha1: demo_sha1.cpp sha1.cpp sha1.hpp - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -Wall -Wextra -std=c++11 -o $@ demo_sha1.cpp sha1.cpp + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -Wall -Wextra -std=c++11 -g -O3 -o $@ demo_sha1.cpp test_sha1: test_sha1.cpp test_sha1_file.cpp sha1.hpp - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -Wall -Wextra -std=c++11 -o $@ test_sha1.cpp test_sha1_file.cpp sha1.cpp + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -Wall -Wextra -std=c++11 -g -O3 -o $@ test_sha1.cpp test_sha1_file.cpp check: test_sha1 ./test_sha1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..c2a1ab8 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# SHA-1 implementation in C++ + +## Warning + +Do not use SHA-1 unless you have to. [SHA-1 is practically broken](https://en.wikipedia.org/wiki/SHA-1#Birthday-Near-Collision_Attack_%E2%80%93_first_practical_chosen-prefix_attack). +Use a hash function from the [SHA-2](https://en.wikipedia.org/wiki/SHA-2) or [SHA-3](https://en.wikipedia.org/wiki/SHA-3) family instead. + +- Despite been proven that SHA-1 has collision attacks, SHA-1 is still extensively used, for example in +websocket protocol. + +- For security related purposes one should use a combination of at least two hashes. For example in pseudocode: + - sha2(sha1(data)) not very good + - sha1(data)+sha2(data) better + - sha1(data.part1)+sha2(data.part2)+sha3(data) strong (where data=data.part1+data.part2) + + +## To build the tests +``` +cd tests +mkdir build +cd build +cmake .. +``` + +## License + +100% Public Domain + +## Authors + +- Steve Reid (Original C Code) +- [Bruce Guenter](http://untroubled.org/) (Small changes to fit into bglibs) +- [Volker Diels-Grabsch](https://njh.eu/) (Translation to simpler C++ Code) +- [Eugene Hopkinson](https://riot.so/) (Safety improvements) +- [Zlatko Michailov](http://zlatko.michailov.org) (Header-only library) +- [Dan Machado](dan-machado@yandex.com) (Optimization) diff --git a/README.org b/README.org deleted file mode 100644 index 9c6cb7f..0000000 --- a/README.org +++ /dev/null @@ -1,17 +0,0 @@ -* SHA-1 implementation in C++ - -** Warning - -Do not use SHA-1 unless you have to! [[https://en.wikipedia.org/wiki/SHA-1#Birthday-Near-Collision_Attack_%E2%80%93_first_practical_chosen-prefix_attack][SHA-1 is practically broken]]. Use a hash function from the [[https://en.wikipedia.org/wiki/SHA-2][SHA-2]] or [[https://en.wikipedia.org/wiki/SHA-3][SHA-3]] family instead. - -** License - -100% Public Domain - -** Authors - -- Steve Reid (Original C Code) -- [[http://untroubled.org/][Bruce Guenter]] (Small changes to fit into bglibs) -- [[https://njh.eu/][Volker Diels-Grabsch]] (Translation to simpler C++ Code) -- [[https://riot.so/][Eugene Hopkinson]] (Safety improvements) -- [[http://zlatko.michailov.org][Zlatko Michailov]] (Header-only library) diff --git a/performance/CMakeLists.txt b/performance/CMakeLists.txt new file mode 100644 index 0000000..e418581 --- /dev/null +++ b/performance/CMakeLists.txt @@ -0,0 +1,88 @@ +cmake_minimum_required(VERSION 3.18) +project("SHA1_tests") + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_FLAGS "-Wall") +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin) + +get_filename_component(PARENT_DIR ../ ABSOLUTE) + + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + add_definitions(-D_WIN32) + add_compile_options("/O2") +else() + add_definitions(-D_LINUX) + if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(WARNING "Assuming system in Linux") + endif() + add_compile_options("-O3") + add_compile_options("-g") +endif() + +#--------------------------------------------------------------------- + +include(FetchContent) + +FetchContent_Declare(TimeProfiler + GIT_REPOSITORY "https://github.com/volatilflerovium/time_profiler_visualizer" + GIT_TAG "origin/main" +) + +FetchContent_MakeAvailable(TimeProfiler) + +#--------------------------------------------------------------------- + +add_library(SHA1_lib INTERFACE) + +target_include_directories( + SHA1_lib + INTERFACE + "${CMAKE_SOURCE_DIR}/.." +) + +#--------------------------------------------------------------------- + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + + execute_process(COMMAND ls /tmp/1G_sha1_test_file.txt + ERROR_VARIABLE error + OUTPUT_VARIABLE output + ) + + if(NOT ("${error}" STREQUAL "")) + message("Creating a 1G file of random data in /tmp directory...") + execute_process(COMMAND dd if=/dev/urandom of=/tmp/1G_sha1_test_file.txt bs=1G count=1 iflag=fullblock + ERROR_VARIABLE error + OUTPUT_VARIABLE output + ) + endif() + add_compile_definitions(TEST_FILE="/tmp/1G_sha1_test_file.txt") +endif() + +#--------------------------------------------------------------------- + +set(TEST "big_file_test") + +add_executable( + "${TEST}" + big_file_test.cpp +) + +target_link_libraries( + "${TEST}" + PRIVATE + TimeProfiler + SHA1_lib +) + +target_compile_definitions( + "${TEST}" + PRIVATE + -DENABLE_STOPWATCH +) + +#--------------------------------------------------------------------- diff --git a/performance/README.md b/performance/README.md new file mode 100644 index 0000000..f45d77c --- /dev/null +++ b/performance/README.md @@ -0,0 +1,20 @@ +# Profiling + +Charts were generated using (time_profiler_visualizer)[https://github.com/volatilflerovium/time_profiler_visualizer] + +## Performance + +Performace tests run under Debian 11 with AMD Ryzen7 processor. + +For a file of 1GB + - SHA1 previous version: ~1870ms + - Linux command sha1sum: ~1471ms + - SHA1 optimized: ~1340ms (under Linux with mmap) and ~1530ms (for Linux and Windows using std::fread) + +On a 1 GB file (in seconds) + +[![performance-1GB-file.png](https://i.postimg.cc/GpPT4tPZ/performance-1GB-file.png)](https://postimg.cc/7C6ZcxWV) + +On incremental string (in microseconds) + +[![performance.png](https://i.postimg.cc/J4dN8sK0/performance.png)](https://postimg.cc/YhYGfq5B) diff --git a/performance/big_file_test.cpp b/performance/big_file_test.cpp new file mode 100644 index 0000000..9967e58 --- /dev/null +++ b/performance/big_file_test.cpp @@ -0,0 +1,47 @@ +#include + +#include "sha1.hpp" + +#include "time_profiler/time_profiler.h" + +using Profiler=tprofiler::TimeProfiler; + +//==================================================================== + +int main() +{ + const char* testFile=""; + #ifndef TEST_FILE + std::cout< Header-only library -- Zlatko Michailov + C++ optimization + -- Dan Machado */ #ifndef SHA1_HPP #define SHA1_HPP - -#include -#include -#include -#include -#include #include +#include + +#ifdef _LINUX + #if __has_include() + // for mmap: + #define MMAN_HEADER + + #include + #include + #include + #endif + #include +#else + #include +#endif + +//==================================================================== class SHA1 { -public: - SHA1(); - void update(const std::string &s); - void update(std::istream &is); - std::string final(); - static std::string from_file(const std::string &filename); - -private: - uint32_t digest[5]; - std::string buffer; - uint64_t transforms; + public: + SHA1(); + + /** + * Partially calculate the sha1sum of its argument. The calculation + * is completed by calling final(). Consecutive calls to update() + * will have the effect of calculating the sha1 sum of the concatenated + * string of its arguments. For example: + * SHA1 sha1; + * sha1.update(str1); + * sha1.update(str2); + * sha1.update(str3); + * sha1.final(); + * is the same than + * sha1.update(str1 + str2 + str3); + * sha1.final(); + * which is the same as (using the function operator) + * sha1(str1 + str2 + str3) + * + * @param cstr null terminated string + * + * */ + void update(const char* cstr); + void update(const std::string& str); + void update(const std::string&& str); + + /** + * Complete the calculation of the sha1sum initiated by calls to + * SHA1::update. + * + * @return return the sha1sum of the string (or strings) + * fed by calls to SHA1::update. + * */ + std::string final(); + + static std::string from_file(const std::string&); + + /** + * Function call operator to calculate the sha1sum of + * a string. Return the sha1sum of the given string + * + * @param str a null terminated string or a std::string. + * + * @return sha1 sum of the str. + * + * */ + std::string operator()(const char* str); + std::string operator()(const std::string& str); + + /** + * Function call operator to calculate the sha1sum of a file. + * It returns false if an error occurs while opening the file and + * set the paramenter hashSum as the empty string, otherwise it return + * true and it sets the sha1sum of the file in the paramenter hashSum. + * + * @param fileName the name of a valid file + * + * @param[out] hashSum reference to a std::string where to copy the hashsum + * of the file. If a error occurred, it is set as the empty string + * + * @return true if the sha1sum was calculated, false if an error occurs + * while opening the file. + * */ + bool operator()(const char* fileName, std::string& hashSum); + bool operator()(const std::string& fileName, std::string& hashSum); + + /** + * Retrieve the description of the error that occurred when trying + * to get the sha1sum of a file. + * + * */ + std::string getError(); + + private: + static const size_t BLOCK_INTS = 16; /* number of 32bit integers per SHA1 block */ + static const size_t BLOCK_BYTES = BLOCK_INTS * 4; + static const size_t DIGEST_SIZE = 5; + + uint32_t m_dataBlock[BLOCK_INTS]; + uint8_t m_buffer[BLOCK_BYTES]; + uint32_t m_digestB[DIGEST_SIZE]; + uint32_t m_digest[DIGEST_SIZE]; + uint64_t m_transforms; + size_t m_dataSize; + int m_lastError; + const uint8_t* m_bufferPtr; + + void reset(); + + template + uint32_t rol(uint32_t value); + + void blk(size_t i); + + /* + * R0, R1, R2, R3, R4 are the different operations used in SHA1 + */ + template + void R0(const size_t i); + + template + void R1(const size_t i); + + template + void R2(const size_t i); + + template + void R3(const size_t i); + + template + void R4(const size_t i); + + void computeHash(const char* dataPtr, const size_t dataSize); + + std::string fileSha1Sum(const char* fileName); + + /* + * Hash a single 512-bit block. This is the core of the algorithm. + */ + void transform(); + void buffer_to_block(); }; +//==================================================================== -static const size_t BLOCK_INTS = 16; /* number of 32bit integers per SHA1 block */ -static const size_t BLOCK_BYTES = BLOCK_INTS * 4; - - -inline static void reset(uint32_t digest[], std::string &buffer, uint64_t &transforms) +inline void SHA1::reset() { /* SHA1 initialization constants */ - digest[0] = 0x67452301; - digest[1] = 0xefcdab89; - digest[2] = 0x98badcfe; - digest[3] = 0x10325476; - digest[4] = 0xc3d2e1f0; + m_digestB[0] = 0x67452301; + m_digestB[1] = 0xefcdab89; + m_digestB[2] = 0x98badcfe; + m_digestB[3] = 0x10325476; + m_digestB[4] = 0xc3d2e1f0; /* Reset counters */ - buffer = ""; - transforms = 0; + m_transforms = 0; + m_dataSize=0; + m_bufferPtr=m_buffer; } -inline static uint32_t rol(const uint32_t value, const size_t bits) +template +inline uint32_t SHA1::rol(uint32_t value) { - return (value << bits) | (value >> (32 - bits)); + return (value << N) | (value >> M); } - -inline static uint32_t blk(const uint32_t block[BLOCK_INTS], const size_t i) +inline void SHA1::blk(size_t i) { - return rol(block[(i+13)&15] ^ block[(i+8)&15] ^ block[(i+2)&15] ^ block[i], 1); + m_dataBlock[i]=rol<1, 31>(m_dataBlock[(i+13)&15] ^ m_dataBlock[(i+8)&15] ^ m_dataBlock[(i+2)&15] ^ m_dataBlock[i]); } +template +inline void SHA1::R0(const size_t i) +{ + m_digest[z] =m_digest[z]+ ((m_digest[w]&(m_digest[x]^m_digest[y]))^m_digest[y]) + m_dataBlock[i] + 0x5a827999 + rol<5, 27>(m_digest[v]); + m_digest[w] = rol<30, 2>(m_digest[w]); +} -/* - * (R0+R1), R2, R3, R4 are the different operations used in SHA1 - */ +template +inline void SHA1::R1(const size_t i) +{ + blk(i); + R0(i); +} -inline static void R0(const uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +template +inline void SHA1::R2(const size_t i) { - z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5); - w = rol(w, 30); + blk(i); + m_digest[z] =m_digest[z]+ (m_digest[w]^m_digest[x]^m_digest[y]) + m_dataBlock[i] + 0x6ed9eba1 + rol<5, 27>(m_digest[v]); + m_digest[w] = rol<30, 2>(m_digest[w]); } +template +inline void SHA1::R3(const size_t i) +{ + blk(i); + m_digest[z] =m_digest[z]+ (((m_digest[w]|m_digest[x])&m_digest[y])|(m_digest[w]&m_digest[x])) + m_dataBlock[i] + 0x8f1bbcdc + rol<5, 27>(m_digest[v]); + m_digest[w] = rol<30, 2>(m_digest[w]); +} -inline static void R1(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +template +inline void SHA1::R4(const size_t i) { - block[i] = blk(block, i); - z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5); - w = rol(w, 30); + blk(i); + m_digest[z] =m_digest[z]+ (m_digest[w]^m_digest[x]^m_digest[y]) + m_dataBlock[i] + 0xca62c1d6 + rol<5, 27>(m_digest[v]); + m_digest[w] = rol<30, 2>(m_digest[w]); } -inline static void R2(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +inline void SHA1::transform() { - block[i] = blk(block, i); - z += (w^x^y) + block[i] + 0x6ed9eba1 + rol(v, 5); - w = rol(w, 30); + std::memcpy(m_digest, m_digestB, DIGEST_SIZE*sizeof(uint32_t)); + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0<0, 1, 2, 3, 4>( 0); + R0<4, 0, 1, 2, 3>( 1); + R0<3, 4, 0, 1, 2>( 2); + R0<2, 3, 4, 0, 1>( 3); + R0<1, 2, 3, 4, 0>( 4); + R0<0, 1, 2, 3, 4>( 5); + R0<4, 0, 1, 2, 3>( 6); + R0<3, 4, 0, 1, 2>( 7); + R0<2, 3, 4, 0, 1>( 8); + R0<1, 2, 3, 4, 0>( 9); + R0<0, 1, 2, 3, 4>(10); + R0<4, 0, 1, 2, 3>(11); + R0<3, 4, 0, 1, 2>(12); + R0<2, 3, 4, 0, 1>(13); + R0<1, 2, 3, 4, 0>(14); + R0<0, 1, 2, 3, 4>(15); + R1<4, 0, 1, 2, 3>( 0); + R1<3, 4, 0, 1, 2>( 1); + R1<2, 3, 4, 0, 1>( 2); + R1<1, 2, 3, 4, 0>( 3); + R2<0, 1, 2, 3, 4>( 4); + R2<4, 0, 1, 2, 3>( 5); + R2<3, 4, 0, 1, 2>( 6); + R2<2, 3, 4, 0, 1>( 7); + R2<1, 2, 3, 4, 0>( 8); + R2<0, 1, 2, 3, 4>( 9); + R2<4, 0, 1, 2, 3>(10); + R2<3, 4, 0, 1, 2>(11); + R2<2, 3, 4, 0, 1>(12); + R2<1, 2, 3, 4, 0>(13); + R2<0, 1, 2, 3, 4>(14); + R2<4, 0, 1, 2, 3>(15); + R2<3, 4, 0, 1, 2>( 0); + R2<2, 3, 4, 0, 1>( 1); + R2<1, 2, 3, 4, 0>( 2); + R2<0, 1, 2, 3, 4>( 3); + R2<4, 0, 1, 2, 3>( 4); + R2<3, 4, 0, 1, 2>( 5); + R2<2, 3, 4, 0, 1>( 6); + R2<1, 2, 3, 4, 0>( 7); + R3<0, 1, 2, 3, 4>( 8); + R3<4, 0, 1, 2, 3>( 9); + R3<3, 4, 0, 1, 2>(10); + R3<2, 3, 4, 0, 1>(11); + R3<1, 2, 3, 4, 0>(12); + R3<0, 1, 2, 3, 4>(13); + R3<4, 0, 1, 2, 3>(14); + R3<3, 4, 0, 1, 2>(15); + R3<2, 3, 4, 0, 1>( 0); + R3<1, 2, 3, 4, 0>( 1); + R3<0, 1, 2, 3, 4>( 2); + R3<4, 0, 1, 2, 3>( 3); + R3<3, 4, 0, 1, 2>( 4); + R3<2, 3, 4, 0, 1>( 5); + R3<1, 2, 3, 4, 0>( 6); + R3<0, 1, 2, 3, 4>( 7); + R3<4, 0, 1, 2, 3>( 8); + R3<3, 4, 0, 1, 2>( 9); + R3<2, 3, 4, 0, 1>(10); + R3<1, 2, 3, 4, 0>(11); + R4<0, 1, 2, 3, 4>(12); + R4<4, 0, 1, 2, 3>(13); + R4<3, 4, 0, 1, 2>(14); + R4<2, 3, 4, 0, 1>(15); + R4<1, 2, 3, 4, 0>( 0); + R4<0, 1, 2, 3, 4>( 1); + R4<4, 0, 1, 2, 3>( 2); + R4<3, 4, 0, 1, 2>( 3); + R4<2, 3, 4, 0, 1>( 4); + R4<1, 2, 3, 4, 0>( 5); + R4<0, 1, 2, 3, 4>( 6); + R4<4, 0, 1, 2, 3>( 7); + R4<3, 4, 0, 1, 2>( 8); + R4<2, 3, 4, 0, 1>( 9); + R4<1, 2, 3, 4, 0>(10); + R4<0, 1, 2, 3, 4>(11); + R4<4, 0, 1, 2, 3>(12); + R4<3, 4, 0, 1, 2>(13); + R4<2, 3, 4, 0, 1>(14); + R4<1, 2, 3, 4, 0>(15); + + /* Add the working vars back into m_digestB[] */ + m_digestB[0]=m_digestB[0]+m_digest[0]; + m_digestB[1]=m_digestB[1]+m_digest[1]; + m_digestB[2]=m_digestB[2]+m_digest[2]; + m_digestB[3]=m_digestB[3]+m_digest[3]; + m_digestB[4]=m_digestB[4]+m_digest[4]; + + /* Count the number of transformations */ + m_transforms++; } -inline static void R3(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +inline void SHA1::buffer_to_block() { - block[i] = blk(block, i); - z += (((w|x)&y)|(w&x)) + block[i] + 0x8f1bbcdc + rol(v, 5); - w = rol(w, 30); + m_dataBlock[0] = m_bufferPtr[3] | m_bufferPtr[2]<<8 | m_bufferPtr[1]<<16 | m_bufferPtr[0]<<24; + m_dataBlock[1] = m_bufferPtr[7] | m_bufferPtr[6]<<8 | m_bufferPtr[5]<<16 | m_bufferPtr[4]<<24; + m_dataBlock[2] = m_bufferPtr[11] | m_bufferPtr[10]<<8 | m_bufferPtr[9]<<16 | m_bufferPtr[8]<<24; + m_dataBlock[3] = m_bufferPtr[15] | m_bufferPtr[14]<<8 | m_bufferPtr[13]<<16 | m_bufferPtr[12]<<24; + m_dataBlock[4] = m_bufferPtr[19] | m_bufferPtr[18]<<8 | m_bufferPtr[17]<<16 | m_bufferPtr[16]<<24; + m_dataBlock[5] = m_bufferPtr[23] | m_bufferPtr[22]<<8 | m_bufferPtr[21]<<16 | m_bufferPtr[20]<<24; + m_dataBlock[6] = m_bufferPtr[27] | m_bufferPtr[26]<<8 | m_bufferPtr[25]<<16 | m_bufferPtr[24]<<24; + m_dataBlock[7] = m_bufferPtr[31] | m_bufferPtr[30]<<8 | m_bufferPtr[29]<<16 | m_bufferPtr[28]<<24; + m_dataBlock[8] = m_bufferPtr[35] | m_bufferPtr[34]<<8 | m_bufferPtr[33]<<16 | m_bufferPtr[32]<<24; + m_dataBlock[9] = m_bufferPtr[39] | m_bufferPtr[38]<<8 | m_bufferPtr[37]<<16 | m_bufferPtr[36]<<24; + m_dataBlock[10] = m_bufferPtr[43] | m_bufferPtr[42]<<8 | m_bufferPtr[41]<<16 | m_bufferPtr[40]<<24; + m_dataBlock[11] = m_bufferPtr[47] | m_bufferPtr[46]<<8 | m_bufferPtr[45]<<16 | m_bufferPtr[44]<<24; + m_dataBlock[12] = m_bufferPtr[51] | m_bufferPtr[50]<<8 | m_bufferPtr[49]<<16 | m_bufferPtr[48]<<24; + m_dataBlock[13] = m_bufferPtr[55] | m_bufferPtr[54]<<8 | m_bufferPtr[53]<<16 | m_bufferPtr[52]<<24; + m_dataBlock[14] = m_bufferPtr[59] | m_bufferPtr[58]<<8 | m_bufferPtr[57]<<16 | m_bufferPtr[56]<<24; + m_dataBlock[15] = m_bufferPtr[63] | m_bufferPtr[62]<<8 | m_bufferPtr[61]<<16 | m_bufferPtr[60]<<24; } +inline SHA1::SHA1() +{ + reset(); +} -inline static void R4(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +inline void SHA1::update(const char* cstr) { - block[i] = blk(block, i); - z += (w^x^y) + block[i] + 0xca62c1d6 + rol(v, 5); - w = rol(w, 30); + computeHash(cstr, std::strlen(cstr)); } +inline void SHA1::update(const std::string& str) +{ + computeHash(str.c_str(), str.length()); +} + +inline void SHA1::update(const std::string&& str) +{ + computeHash(str.c_str(), str.length()); +} /* - * Hash a single 512-bit block. This is the core of the algorithm. + * Add padding and return the message m_digestB. */ - -inline static void transform(uint32_t digest[], uint32_t block[BLOCK_INTS], uint64_t &transforms) +inline std::string SHA1::final() { - /* Copy digest[] to working vars */ - uint32_t a = digest[0]; - uint32_t b = digest[1]; - uint32_t c = digest[2]; - uint32_t d = digest[3]; - uint32_t e = digest[4]; + /* Total number of hashed bits */ + uint64_t total_bits = (m_transforms*BLOCK_BYTES + m_dataSize) * 8; - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(block, a, b, c, d, e, 0); - R0(block, e, a, b, c, d, 1); - R0(block, d, e, a, b, c, 2); - R0(block, c, d, e, a, b, 3); - R0(block, b, c, d, e, a, 4); - R0(block, a, b, c, d, e, 5); - R0(block, e, a, b, c, d, 6); - R0(block, d, e, a, b, c, 7); - R0(block, c, d, e, a, b, 8); - R0(block, b, c, d, e, a, 9); - R0(block, a, b, c, d, e, 10); - R0(block, e, a, b, c, d, 11); - R0(block, d, e, a, b, c, 12); - R0(block, c, d, e, a, b, 13); - R0(block, b, c, d, e, a, 14); - R0(block, a, b, c, d, e, 15); - R1(block, e, a, b, c, d, 0); - R1(block, d, e, a, b, c, 1); - R1(block, c, d, e, a, b, 2); - R1(block, b, c, d, e, a, 3); - R2(block, a, b, c, d, e, 4); - R2(block, e, a, b, c, d, 5); - R2(block, d, e, a, b, c, 6); - R2(block, c, d, e, a, b, 7); - R2(block, b, c, d, e, a, 8); - R2(block, a, b, c, d, e, 9); - R2(block, e, a, b, c, d, 10); - R2(block, d, e, a, b, c, 11); - R2(block, c, d, e, a, b, 12); - R2(block, b, c, d, e, a, 13); - R2(block, a, b, c, d, e, 14); - R2(block, e, a, b, c, d, 15); - R2(block, d, e, a, b, c, 0); - R2(block, c, d, e, a, b, 1); - R2(block, b, c, d, e, a, 2); - R2(block, a, b, c, d, e, 3); - R2(block, e, a, b, c, d, 4); - R2(block, d, e, a, b, c, 5); - R2(block, c, d, e, a, b, 6); - R2(block, b, c, d, e, a, 7); - R3(block, a, b, c, d, e, 8); - R3(block, e, a, b, c, d, 9); - R3(block, d, e, a, b, c, 10); - R3(block, c, d, e, a, b, 11); - R3(block, b, c, d, e, a, 12); - R3(block, a, b, c, d, e, 13); - R3(block, e, a, b, c, d, 14); - R3(block, d, e, a, b, c, 15); - R3(block, c, d, e, a, b, 0); - R3(block, b, c, d, e, a, 1); - R3(block, a, b, c, d, e, 2); - R3(block, e, a, b, c, d, 3); - R3(block, d, e, a, b, c, 4); - R3(block, c, d, e, a, b, 5); - R3(block, b, c, d, e, a, 6); - R3(block, a, b, c, d, e, 7); - R3(block, e, a, b, c, d, 8); - R3(block, d, e, a, b, c, 9); - R3(block, c, d, e, a, b, 10); - R3(block, b, c, d, e, a, 11); - R4(block, a, b, c, d, e, 12); - R4(block, e, a, b, c, d, 13); - R4(block, d, e, a, b, c, 14); - R4(block, c, d, e, a, b, 15); - R4(block, b, c, d, e, a, 0); - R4(block, a, b, c, d, e, 1); - R4(block, e, a, b, c, d, 2); - R4(block, d, e, a, b, c, 3); - R4(block, c, d, e, a, b, 4); - R4(block, b, c, d, e, a, 5); - R4(block, a, b, c, d, e, 6); - R4(block, e, a, b, c, d, 7); - R4(block, d, e, a, b, c, 8); - R4(block, c, d, e, a, b, 9); - R4(block, b, c, d, e, a, 10); - R4(block, a, b, c, d, e, 11); - R4(block, e, a, b, c, d, 12); - R4(block, d, e, a, b, c, 13); - R4(block, c, d, e, a, b, 14); - R4(block, b, c, d, e, a, 15); - - /* Add the working vars back into digest[] */ - digest[0] += a; - digest[1] += b; - digest[2] += c; - digest[3] += d; - digest[4] += e; + if(m_dataSize(0x80); + buffer_to_block(); + } - /* Count the number of transformations */ - transforms++; + if (m_dataSize+1 > BLOCK_BYTES - 8){ + transform(); + std::memset(m_dataBlock, 0, sizeof(uint32_t)*(BLOCK_INTS - 2)); + } + + /* Append total_bits, split this uint64_t into two uint32_t */ + m_dataBlock[BLOCK_INTS - 1] = static_cast(total_bits); + m_dataBlock[BLOCK_INTS - 2] = static_cast(total_bits >> 32); + transform(); + + /* Hex std::string */ + char resultBuffer[41]; + for(size_t i = 0; i < DIGEST_SIZE; i++){ + sprintf(resultBuffer+8*i, "%08x", m_digestB[i]); + } + reset(); + return resultBuffer; } +inline std::string SHA1::from_file(const std::string& filename) +{ + std::string hash; + SHA1 sha1; + sha1(filename, hash); + return hash; +} -inline static void buffer_to_block(const std::string &buffer, uint32_t block[BLOCK_INTS]) +inline std::string SHA1::operator()(const char* cstr) { - /* Convert the std::string (byte buffer) to a uint32_t array (MSB) */ - for (size_t i = 0; i < BLOCK_INTS; i++) - { - block[i] = (buffer[4*i+3] & 0xff) - | (buffer[4*i+2] & 0xff)<<8 - | (buffer[4*i+1] & 0xff)<<16 - | (buffer[4*i+0] & 0xff)<<24; - } + reset(); + computeHash(cstr, std::strlen(cstr)); + return final(); } +inline std::string SHA1::operator()(const std::string& str) +{ + reset(); + computeHash(str.c_str(), str.length()); + return final(); +} -inline SHA1::SHA1() + +inline bool SHA1::operator()(const char* fileName, std::string& hashSum) { - reset(digest, buffer, transforms); + reset(); + hashSum=fileSha1Sum(fileName); + return m_lastError==0; +} + +inline bool SHA1::operator()(const std::string& fileName, std::string& hashSum) +{ + return operator()(fileName.c_str(), hashSum); } -inline void SHA1::update(const std::string &s) +inline std::string SHA1::getError() { - std::istringstream is(s); - update(is); + return std::strerror(m_lastError); } -inline void SHA1::update(std::istream &is) +inline void SHA1::computeHash(const char* dataPtr, size_t dataSize) { - while (true) - { - char sbuf[BLOCK_BYTES]; - is.read(sbuf, BLOCK_BYTES - buffer.size()); - buffer.append(sbuf, (std::size_t)is.gcount()); - if (buffer.size() != BLOCK_BYTES) - { - return; + size_t idx=0; + size_t newDataSize=dataSize; + size_t copyB=BLOCK_BYTES-m_dataSize; + + auto update=[&idx, ©B, &newDataSize, this](){ + idx=idx+copyB; + newDataSize=newDataSize-copyB; + m_dataSize=m_dataSize+copyB; + }; + + while(true){ + if(copyB>newDataSize){ + copyB=newDataSize; + m_bufferPtr=m_buffer; + std::memcpy(m_buffer+m_dataSize, dataPtr+idx, copyB); + + if(m_dataSize!=BLOCK_BYTES){ + update(); + break; + } } - uint32_t block[BLOCK_INTS]; - buffer_to_block(buffer, block); - transform(digest, block, transforms); - buffer.clear(); + else{//copyB==BLOCK_BYTES + m_bufferPtr=reinterpret_cast(dataPtr+idx); + } + update(); + buffer_to_block(); + transform(); + + copyB=BLOCK_BYTES; + m_dataSize=0; } } +#ifdef MMAN_HEADER -/* - * Add padding and return the message digest. - */ - -inline std::string SHA1::final() +inline std::string SHA1::fileSha1Sum(const char* fileName) { - /* Total number of hashed bits */ - uint64_t total_bits = (transforms*BLOCK_BYTES + buffer.size()) * 8; - - /* Padding */ - buffer += (char)0x80; - size_t orig_size = buffer.size(); - while (buffer.size() < BLOCK_BYTES) - { - buffer += (char)0x00; + m_lastError=0; + + if(access(fileName, F_OK)<0){ + m_lastError=errno; + return ""; + } + if(access(fileName, R_OK)<0){ + m_lastError=errno; + return ""; } - uint32_t block[BLOCK_INTS]; - buffer_to_block(buffer, block); + int fd= open(fileName, O_RDONLY); + struct stat sb; + if(fstat(fd, &sb) == -1) { + close(fd); + m_lastError=errno; + return ""; + } - if (orig_size > BLOCK_BYTES - 8) - { - transform(digest, block, transforms); - for (size_t i = 0; i < BLOCK_INTS - 2; i++) - { - block[i] = 0; + if(sb.st_size>0){ + void* data=mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if(data==MAP_FAILED){ + close(fd); + m_lastError=errno; + return ""; + } + else{ + computeHash(static_cast(data), sb.st_size); } + munmap(data, sb.st_size); } + close(fd); + return final(); +} - /* Append total_bits, split this uint64_t into two uint32_t */ - block[BLOCK_INTS - 1] = (uint32_t)total_bits; - block[BLOCK_INTS - 2] = (uint32_t)(total_bits >> 32); - transform(digest, block, transforms); +#else // for Windows or Linux - /* Hex std::string */ - std::ostringstream result; - for (size_t i = 0; i < sizeof(digest) / sizeof(digest[0]); i++) - { - result << std::hex << std::setfill('0') << std::setw(8); - result << digest[i]; +inline std::string SHA1::fileSha1Sum(const char* fileName) +{ + m_lastError=0; + + if(access(fileName, F_OK)<0){ + m_lastError=errno; + return ""; } - /* Reset for next run */ - reset(digest, buffer, transforms); + if(access(fileName, R_OK)<0){ + m_lastError=errno; + return ""; + } - return result.str(); -} + std::FILE* fd = std::fopen(fileName, "r"); + if(fd==nullptr){ + m_lastError=errno; + return ""; + } + const int p=3; + char buffer[p*BLOCK_BYTES]; -inline std::string SHA1::from_file(const std::string &filename) -{ - std::ifstream stream(filename.c_str(), std::ios::binary); - SHA1 checksum; - checksum.update(stream); - return checksum.final(); + while(true){ + auto dataSize = std::fread(buffer, 1, p*BLOCK_BYTES, fd); + if(dataSize==0){ + break; + } + computeHash(buffer, dataSize); + } + + std::fclose(fd); + return final(); } +#endif + +//==================================================================== #endif /* SHA1_HPP */