From 3ec48ec5b4c5c998096d9d96218c4b140f3fc01d Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Tue, 5 Jul 2016 08:40:09 -0400 Subject: [PATCH] support CryptoAPI on windows, for SHA-1 hashing (#882) support CryptoAPI on windows, for SHA-1 hashing. don't always use openssl in test build configurations --- .travis.yml | 20 +++--- ChangeLog | 1 + Jamfile | 2 +- appveyor.yml | 44 +++++++------ include/libtorrent/config.hpp | 11 ++++ include/libtorrent/hasher.hpp | 19 +++--- simulation/Jamfile | 1 + src/hasher.cpp | 121 +++++++++++++++++++++++++++++----- 8 files changed, 165 insertions(+), 54 deletions(-) diff --git a/.travis.yml b/.travis.yml index 788cece21..f1887ef38 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: cpp matrix: include: - env: variant=test_release coverage=1 toolset=gcc-coverage - - env: variant=test_debug sim=1 toolset=gcc + - env: variant=test_debug sim=1 ssl=openssl crypto=libcrypto toolset=gcc - env: variant=test_barebones toolset=gcc - env: analyze=1 autotools=1 toolset=gcc - os: osx @@ -11,7 +11,7 @@ matrix: env: variant=test_release docs=1 toolset=darwin - os: osx osx_image: xcode6.4 - env: variant=test_debug toolset=darwin + env: variant=test_debug ssl=openssl crypto=libcrypto toolset=darwin - os: osx osx_image: xcode6.4 env: variant=test_barebones toolset=darwin @@ -52,6 +52,8 @@ addons: before_install: - git submodule update --init --recursive + - 'if [[ $ssl == "" ]]; then export ssl=off; fi' + - 'if [[ $crypto == "" ]]; then export crypto=built-in; fi' - 'if [[ $TRAVIS_OS_NAME == "osx" && "$variant" != "" ]]; then brew update > /dev/null && brew install --quiet ccache boost-build boost-python; fi' - 'if [ "$docs" = "1" ]; then brew install --quiet https://raw.githubusercontent.com/catap/homebrew/docutils/Library/Formula/docutils.rb; @@ -120,7 +122,7 @@ script: # libtorrent is the name of the test suite target - cd test - 'if [ "$variant" != "" ]; then - bjam -j3 warnings-as-errors=on variant=$variant -l900 $toolset libtorrent test_natpmp enum_if && + bjam -j3 warnings-as-errors=on ssl=$ssl crypto=$crypto variant=$variant -l900 $toolset libtorrent test_natpmp enum_if && if [ "$coverage" == "1" ]; then codecov --root .. --gcov-exec gcov-5; fi; @@ -128,13 +130,13 @@ script: - cd ../examples - 'if [ "$variant" != "" ]; then - bjam -j3 warnings-as-errors=on variant=$variant picker-debugging=on $toolset link=shared; + bjam -j3 warnings-as-errors=on ssl=$ssl crypto=$crypto variant=$variant picker-debugging=on $toolset link=shared; fi' - cd .. - cd tools - 'if [ "$variant" != "" ]; then - bjam -j3 warnings-as-errors=on variant=$variant picker-debugging=on $toolset link=shared; + bjam -j3 warnings-as-errors=on ssl=$ssl crypto=$crypto variant=$variant picker-debugging=on $toolset link=shared; fi' - cd .. @@ -143,12 +145,12 @@ script: # as the main library, so we cannot stage them to the same directory # here we specify the temporary lib dir as a path to look for the main library - 'if [ "$variant" != "" ]; then - bjam -j3 warnings-as-errors=on picker-debugging=on link=shared variant=$variant $toolset install location=./lib; + bjam -j3 warnings-as-errors=on ssl=$ssl crypto=$crypto picker-debugging=on link=shared variant=$variant $toolset install location=./lib; fi' - cd bindings/python - 'if [ "$variant" != "" ]; then - bjam -j3 warnings-as-errors=on variant=$variant picker-debugging=on $toolset stage_module libtorrent-link=shared install-type=LIB dll-path=../../lib && + bjam -j3 warnings-as-errors=on ssl=$ssl crypto=$crypto variant=$variant picker-debugging=on $toolset stage_module libtorrent-link=shared install-type=LIB dll-path=../../lib && LD_LIBRARY_PATH=../../lib DYLD_LIBRARY_PATH=../../lib python test.py; fi' - cd ../..; @@ -156,7 +158,7 @@ script: # simulation - cd simulation - 'if [[ "$variant" != "" && "$sim" == "1" ]]; then - bjam -j2 crypto=built-in warnings-as-errors=on $toolset; + bjam -j2 crypto=built-in ssl=off warnings-as-errors=on $toolset; fi' - cd .. @@ -195,6 +197,6 @@ script: - cd test - 'if [ $arch == "arm" ]; then - bjam arm-tests warnings-as-errors=on variant=test_arm $toolset target-os=linux link=static testing.launcher="sudo cp -R bin rootfs/; sudo chroot rootfs"; + bjam arm-tests warnings-as-errors=on ssl=$ssl crypto=$crypto variant=test_arm $toolset target-os=linux link=static testing.launcher="sudo cp -R bin rootfs/; sudo chroot rootfs"; fi' - cd .. diff --git a/ChangeLog b/ChangeLog index 7f4747ec9..7e0ea3aa5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * support windows' CryptoAPI for SHA-1 * separated ssl and crypto options in build * remove lazy-bitfield feature * simplified suggest-read-cache feature to not depend on disk threads diff --git a/Jamfile b/Jamfile index 1a574161b..d1f4eb46f 100644 --- a/Jamfile +++ b/Jamfile @@ -512,7 +512,7 @@ variant test_release : release off ; variant test_debug : debug - : openssl libcrypto on on + : on on debug full shared on on multi on diff --git a/appveyor.yml b/appveyor.yml index e59358e41..efdd7f687 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,36 +12,34 @@ environment: compiler: msvc-14.0 model: 64 sim: 1 - linkflags: '"/LIBPATH:C:\\openssl-1.0.1p-vs2015\\lib64"' - include: '"c:\\openssl-1.0.1p-vs2015\\include"' - variant: test_debug compiler: msvc-14.0 model: 32 - linkflags: '"/LIBPATH:C:\\openssl-1.0.1p-vs2015\\lib"' - include: '"c:\\openssl-1.0.1p-vs2015\\include"' - variant: test_release compiler: msvc-14.0 model: 64 python: 1 - linkflags: '"/LIBPATH:C:\\openssl-1.0.1p-vs2015\\lib64"' - include: '"c:\\openssl-1.0.1p-vs2015\\include"' + ssl: openssl + crypto: libcrypto + linkflags: 'linkflags="/LIBPATH:C:\\openssl-1.0.1p-vs2015\\lib64"' + include: 'include="c:\\openssl-1.0.1p-vs2015\\include"' - variant: test_debug compiler: gcc model: 32 - linkflags: '"-LC:\\OpenSSL-Win32\\lib"' - include: '"c:\\OpenSSL-Win32\\include"' + ssl: openssl + crypto: libcrypto + linkflags: 'linkflags="-LC:\\OpenSSL-Win32\\lib"' + include: 'include="c:\\OpenSSL-Win32\\include"' install: - if defined sim ( git submodule update --init --recursive ) - set ROOT_DIRECTORY=%CD% - cd %ROOT_DIRECTORY% -- if %compiler% == msvc-14.0 ( - if not exist openssl-1.0.1p-vs2015.7z ( - echo downloading openssl-2015 - & appveyor DownloadFile "http://www.npcglib.org/~stathis/downloads/openssl-1.0.1p-vs2015.7z" - ) +- if %compiler% == msvc-14.0 if defined ssl if not exist openssl-1.0.1p-vs2015.7z ( + echo downloading openssl-2015 + & appveyor DownloadFile "http://www.npcglib.org/~stathis/downloads/openssl-1.0.1p-vs2015.7z" ) -- if %compiler% == msvc-14.0 ( +- if %compiler% == msvc-14.0 if defined ssl ( echo extracting openssl-2015 & 7z x -oc:\ -aoa openssl-1.0.1p-vs2015.7z > nul & copy c:\openssl-1.0.1p-vs2015\lib64\ssleay32MT.lib c:\openssl-1.0.1p-vs2015\lib64\ssleay32.lib @@ -49,6 +47,10 @@ install: & copy c:\openssl-1.0.1p-vs2015\lib\ssleay32MT.lib c:\openssl-1.0.1p-vs2015\lib\ssleay32.lib & copy c:\openssl-1.0.1p-vs2015\lib\libeay32MT.lib c:\openssl-1.0.1p-vs2015\lib\libeay32.lib ) +- if not defined ssl ( set ssl=off ) +- if not defined crypto ( set crypto=built-in ) +- if not defined linkflags ( set linkflags="" ) +- if not defined include ( set include="" ) - cd %ROOT_DIRECTORY% - set BOOST_ROOT=c:\Libraries\boost_1_59_0 - set BOOST_BUILD_PATH=%BOOST_ROOT%\tools\build @@ -72,34 +74,34 @@ cache: build_script: # examples - cd %ROOT_DIRECTORY%\examples -- b2.exe --hash warnings-as-errors=on -j2 %compiler% address-model=%model% variant=%variant% picker-debugging=on linkflags=%linkflags% include=%include% link=shared +- b2.exe --hash warnings-as-errors=on -j2 %compiler% ssl=%ssl% crypto=%crypto% address-model=%model% variant=%variant% picker-debugging=on %linkflags% %include% link=shared # tools - cd %ROOT_DIRECTORY%\tools -- b2.exe --hash warnings-as-errors=on -j2 %compiler% address-model=%model% variant=%variant% picker-debugging=on linkflags=%linkflags% include=%include% link=shared +- b2.exe --hash warnings-as-errors=on -j2 %compiler% ssl=%ssl% crypto=%crypto% address-model=%model% variant=%variant% picker-debugging=on %linkflags% %include% link=shared # test - cd %ROOT_DIRECTORY%\test -- b2.exe --hash warnings-as-errors=on -j2 address-model=%model% win-tests test_upnp test_natpmp %compiler% variant=%variant% picker-debugging=on link=shared linkflags=%linkflags% include=%include% testing.execute=off +- b2.exe --hash warnings-as-errors=on -j2 %compiler% ssl=%ssl% crypto=%crypto% address-model=%model% win-tests test_upnp test_natpmp variant=%variant% picker-debugging=on link=shared %linkflags% %include% testing.execute=off # python binding - cd %ROOT_DIRECTORY%\bindings\python # we use 64 bit python builds -- if defined python ( b2.exe --hash warnings-as-errors=on -j2 %compiler% address-model=%model% stage_module install-dependencies=on variant=%variant% picker-debugging=on libtorrent-link=shared linkflags=%linkflags% include=%include% ) +- if defined python ( b2.exe --hash warnings-as-errors=on -j2 %compiler% ssl=%ssl% crypto=%crypto% address-model=%model% stage_module install-dependencies=on variant=%variant% picker-debugging=on libtorrent-link=shared %linkflags% %include% ) # simulations - if defined sim ( cd %ROOT_DIRECTORY%\simulation - & b2.exe --hash warnings-as-errors=on -j2 link=shared crypto=built-in %compiler% address-model=%model% testing.execute=off + & b2.exe --hash warnings-as-errors=on -j2 link=shared %compiler% ssl=%ssl% crypto=%crypto% address-model=%model% testing.execute=off ) test_script: - cd %ROOT_DIRECTORY%\test # mingw tests crash currently. needs resolving -- if not %compiler% == gcc ( b2.exe --hash warnings-as-errors=on -j2 address-model=%model% win-tests %compiler% variant=%variant% picker-debugging=on link=shared linkflags=%linkflags% include=%include% ) +- if not %compiler% == gcc ( b2.exe --hash warnings-as-errors=on -j2 ssl=%ssl% crypto=%crypto% address-model=%model% win-tests %compiler% variant=%variant% picker-debugging=on link=shared %linkflags% %include% ) - cd %ROOT_DIRECTORY%\bindings\python # we use 64 bit python build - if defined python ( c:\Python35-x64\python.exe test.py ) - cd %ROOT_DIRECTORY%\simulation -- if defined sim ( b2.exe --hash warnings-as-errors=on -j2 link=shared crypto=built-in %compiler% address-model=%model% ) +- if defined sim ( b2.exe --hash warnings-as-errors=on -j2 ssl=%ssl% crypto=%crypto% link=shared crypto=built-in %compiler% address-model=%model% ) diff --git a/include/libtorrent/config.hpp b/include/libtorrent/config.hpp index 95979b542..950f71b25 100644 --- a/include/libtorrent/config.hpp +++ b/include/libtorrent/config.hpp @@ -235,6 +235,13 @@ POSSIBILITY OF SUCH DAMAGE. #ifndef TORRENT_USE_GETIPFORWARDTABLE # define TORRENT_USE_GETIPFORWARDTABLE 1 #endif + +# if !defined TORRENT_USE_LIBCRYPTO && !defined TORRENT_USE_LIBGCRYPT +// unless some other crypto library has been specified, default to the native +// windows CryptoAPI +#define TORRENT_USE_CRYPTOAPI 1 +#endif + #define TORRENT_USE_GETADAPTERSADDRESSES 1 #define TORRENT_HAS_SALEN 0 // windows has its own functions to convert @@ -390,6 +397,10 @@ POSSIBILITY OF SUCH DAMAGE. #define TORRENT_USE_COMMONCRYPTO 0 #endif +#ifndef TORRENT_USE_CRYPTOAPI +#define TORRENT_USE_CRYPTOAPI 0 +#endif + #ifndef TORRENT_HAVE_MMAP #define TORRENT_HAVE_MMAP 0 #endif diff --git a/include/libtorrent/hasher.hpp b/include/libtorrent/hasher.hpp index 78a29e1ac..db35dab18 100644 --- a/include/libtorrent/hasher.hpp +++ b/include/libtorrent/hasher.hpp @@ -33,6 +33,7 @@ POSSIBILITY OF SUCH DAMAGE. #ifndef TORRENT_HASHER_HPP_INCLUDED #define TORRENT_HASHER_HPP_INCLUDED +#include "libtorrent/config.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/config.hpp" #include "libtorrent/assert.hpp" @@ -43,13 +44,14 @@ POSSIBILITY OF SUCH DAMAGE. #include #elif TORRENT_USE_COMMONCRYPTO - #include +#elif TORRENT_USE_CRYPTOAPI +#include + #elif defined TORRENT_USE_LIBCRYPTO -extern "C" -{ +extern "C" { #include } @@ -83,12 +85,10 @@ namespace libtorrent // this is the same as default constructing followed by a call to // ``update(data, len)``. - hasher(const char* data, int len); + hasher(char const* data, int len); -#ifdef TORRENT_USE_LIBGCRYPT - hasher(hasher const& h); - hasher& operator=(hasher const& h); -#endif + hasher(hasher const&); + hasher& operator=(hasher const&); // append the following bytes to what is being hashed hasher& update(std::string const& data) { update(data.c_str(), int(data.size())); return *this; } @@ -97,6 +97,7 @@ namespace libtorrent // returns the SHA-1 digest of the buffers previously passed to // update() and the hasher constructor. sha1_hash final(); + // restore the hasher state to be as if the hasher has just been // default constructed. void reset(); @@ -109,6 +110,8 @@ namespace libtorrent gcry_md_hd_t m_context; #elif TORRENT_USE_COMMONCRYPTO CC_SHA1_CTX m_context; +#elif TORRENT_USE_CRYPTOAPI + HCRYPTHASH m_context; #elif defined TORRENT_USE_LIBCRYPTO SHA_CTX m_context; #else diff --git a/simulation/Jamfile b/simulation/Jamfile index 5e44398ba..a3cc2b15c 100644 --- a/simulation/Jamfile +++ b/simulation/Jamfile @@ -17,6 +17,7 @@ project create_torrent.cpp utils.cpp msvc:/wd4275 + msvc:/wd4005 : default-build multi full diff --git a/src/hasher.cpp b/src/hasher.cpp index 904740be0..52c311a6f 100644 --- a/src/hasher.cpp +++ b/src/hasher.cpp @@ -31,6 +31,35 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "libtorrent/hasher.hpp" +#include "libtorrent/error_code.hpp" + +#if TORRENT_USE_CRYPTOAPI +namespace +{ + HCRYPTPROV make_crypt_provider() + { + using namespace libtorrent; + + HCRYPTPROV ret; + if (CryptAcquireContext(&ret, nullptr, nullptr, PROV_RSA_FULL + , CRYPT_VERIFYCONTEXT) == false) + { +#ifndef BOOST_NO_EXCEPTIONS + throw system_error(error_code(GetLastError(), system_category())); +#else + std::terminate(); +#endif + } + return ret; + } + + HCRYPTPROV get_crypt_provider() + { + static HCRYPTPROV prov = make_crypt_provider(); + return prov; + } +} +#endif namespace libtorrent { @@ -40,6 +69,15 @@ namespace libtorrent gcry_md_open(&m_context, GCRY_MD_SHA1, 0); #elif TORRENT_USE_COMMONCRYPTO CC_SHA1_Init(&m_context); +#elif TORRENT_USE_CRYPTOAPI + if (CryptCreateHash(get_crypt_provider(), CALG_SHA1, 0, 0, &m_context) == false) + { +#ifndef BOOST_NO_EXCEPTIONS + throw system_error(error_code(GetLastError(), system_category())); +#else + std::terminate(); +#endif + } #elif defined TORRENT_USE_LIBCRYPTO SHA1_Init(&m_context); #else @@ -48,22 +86,11 @@ namespace libtorrent } hasher::hasher(const char* data, int len) + : hasher() { TORRENT_ASSERT(data != 0); TORRENT_ASSERT(len > 0); -#ifdef TORRENT_USE_LIBGCRYPT - gcry_md_open(&m_context, GCRY_MD_SHA1, 0); - gcry_md_write(m_context, data, len); -#elif TORRENT_USE_COMMONCRYPTO - CC_SHA1_Init(&m_context); - CC_SHA1_Update(&m_context, reinterpret_cast(data), len); -#elif defined TORRENT_USE_LIBCRYPTO - SHA1_Init(&m_context); - SHA1_Update(&m_context, reinterpret_cast(data), len); -#else - SHA1_init(&m_context); - SHA1_update(&m_context, reinterpret_cast(data), len); -#endif + update(data, len); } #ifdef TORRENT_USE_LIBGCRYPT @@ -74,10 +101,41 @@ namespace libtorrent hasher& hasher::operator=(hasher const& h) { + if (this == &h) return; gcry_md_close(m_context); gcry_md_copy(&m_context, h.m_context); return *this; } +#elif TORRENT_USE_CRYPTOAPI + hasher::hasher(hasher const& h) + { + if (CryptDuplicateHash(h.m_context, 0, 0, &m_context) == false) + { +#ifndef BOOST_NO_EXCEPTIONS + throw system_error(error_code(GetLastError(), system_category())); +#else + std::terminate(); +#endif + } + } + + hasher& hasher::operator=(hasher const& h) + { + if (this == &h) return *this; + CryptDestroyHash(m_context); + if (CryptDuplicateHash(h.m_context, 0, 0, &m_context) == false) + { +#ifndef BOOST_NO_EXCEPTIONS + throw system_error(error_code(GetLastError(), system_category())); +#else + std::terminate(); +#endif + } + return *this; + } +#else + hasher::hasher(hasher const&) = default; + hasher& hasher::operator=(hasher const&) = default; #endif hasher& hasher::update(const char* data, int len) @@ -88,6 +146,15 @@ namespace libtorrent gcry_md_write(m_context, data, len); #elif TORRENT_USE_COMMONCRYPTO CC_SHA1_Update(&m_context, reinterpret_cast(data), len); +#elif TORRENT_USE_CRYPTOAPI + if (CryptHashData(m_context, reinterpret_cast(data), len, 0) == false) + { +#ifndef BOOST_NO_EXCEPTIONS + throw system_error(error_code(GetLastError(), system_category())); +#else + std::terminate(); +#endif + } #elif defined TORRENT_USE_LIBCRYPTO SHA1_Update(&m_context, reinterpret_cast(data), len); #else @@ -104,6 +171,19 @@ namespace libtorrent digest.assign((const char*)gcry_md_read(m_context, 0)); #elif TORRENT_USE_COMMONCRYPTO CC_SHA1_Final(digest.begin(), &m_context); +#elif TORRENT_USE_CRYPTOAPI + + DWORD size = sha1_hash::size; + if (CryptGetHashParam(m_context, HP_HASHVAL + , reinterpret_cast(digest.data()), &size, 0) == false) + { +#ifndef BOOST_NO_EXCEPTIONS + throw system_error(error_code(GetLastError(), system_category())); +#else + std::terminate(); +#endif + } + TORRENT_ASSERT(size == sha1_hash::size); #elif defined TORRENT_USE_LIBCRYPTO SHA1_Final(digest.begin(), &m_context); #else @@ -118,6 +198,16 @@ namespace libtorrent gcry_md_reset(m_context); #elif TORRENT_USE_COMMONCRYPTO CC_SHA1_Init(&m_context); +#elif TORRENT_USE_CRYPTOAPI + CryptDestroyHash(m_context); + if (CryptCreateHash(get_crypt_provider(), CALG_SHA1, 0, 0, &m_context) == false) + { +#ifndef BOOST_NO_EXCEPTIONS + throw system_error(error_code(GetLastError(), system_category())); +#else + std::terminate(); +#endif + } #elif defined TORRENT_USE_LIBCRYPTO SHA1_Init(&m_context); #else @@ -127,10 +217,11 @@ namespace libtorrent hasher::~hasher() { -#ifdef TORRENT_USE_LIBGCRYPT +#if TORRENT_USE_CRYPTOAPI + CryptDestroyHash(m_context); +#elif defined TORRENT_USE_LIBGCRYPT gcry_md_close(m_context); #endif } - }