From 5eb0aa2765edf5c3230f84c852ee1683291b275e Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sat, 22 Mar 2014 12:11:44 -0700 Subject: [PATCH] Add hardened_hash, prevents adversarial inputs --- Builds/VisualStudio2013/Beast.props | 1 + Builds/VisualStudio2013/beast.vcxproj | 15 +- Builds/VisualStudio2013/beast.vcxproj.filters | 6 + beast/threads/impl/Stoppable.cpp | 4 +- beast/utility/Utility.cpp | 1 + beast/utility/empty_base_optimization.h | 5 +- beast/utility/hardened_hash.h | 168 ++++++++++ beast/utility/is_call_possible.h | 84 ++--- .../tests/empty_base_optimization.test.cpp | 6 +- beast/utility/tests/hardened_hash.test.cpp | 311 ++++++++++++++++++ 10 files changed, 550 insertions(+), 51 deletions(-) create mode 100644 beast/utility/hardened_hash.h create mode 100644 beast/utility/tests/hardened_hash.test.cpp diff --git a/Builds/VisualStudio2013/Beast.props b/Builds/VisualStudio2013/Beast.props index 95b35522ca..f1c7c45345 100644 --- a/Builds/VisualStudio2013/Beast.props +++ b/Builds/VisualStudio2013/Beast.props @@ -12,6 +12,7 @@ true false %(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) diff --git a/Builds/VisualStudio2013/beast.vcxproj b/Builds/VisualStudio2013/beast.vcxproj index 9dbccaa521..ee0ff1d65f 100644 --- a/Builds/VisualStudio2013/beast.vcxproj +++ b/Builds/VisualStudio2013/beast.vcxproj @@ -246,6 +246,7 @@ + @@ -756,6 +757,12 @@ true true + + true + true + true + true + true @@ -766,6 +773,8 @@ true true + true + true @@ -892,6 +901,8 @@ true true + true + true true @@ -1301,7 +1312,6 @@ Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) false true @@ -1316,7 +1326,7 @@ Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - $(ProjectDir);%(AdditionalIncludeDirectories) + MultiThreadedDebug Windows @@ -1351,6 +1361,7 @@ true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) $(ProjectDir);%(AdditionalIncludeDirectories) + MultiThreaded Windows diff --git a/Builds/VisualStudio2013/beast.vcxproj.filters b/Builds/VisualStudio2013/beast.vcxproj.filters index eb91f1d7cc..eccb9d4e45 100644 --- a/Builds/VisualStudio2013/beast.vcxproj.filters +++ b/Builds/VisualStudio2013/beast.vcxproj.filters @@ -1227,6 +1227,9 @@ beast\unit_test + + beast\utility + @@ -1700,6 +1703,9 @@ beast\threads\tests + + beast\utility\tests + diff --git a/beast/threads/impl/Stoppable.cpp b/beast/threads/impl/Stoppable.cpp index 35eb60df6b..7c8eb891e8 100644 --- a/beast/threads/impl/Stoppable.cpp +++ b/beast/threads/impl/Stoppable.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ /* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2012, Vinnie Falco Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/beast/utility/Utility.cpp b/beast/utility/Utility.cpp index 9b42c0733c..ef14dce0d9 100644 --- a/beast/utility/Utility.cpp +++ b/beast/utility/Utility.cpp @@ -34,3 +34,4 @@ #include "tests/bassert.test.cpp" #include "tests/empty_base_optimization.test.cpp" +#include "tests/hardened_hash.test.cpp" diff --git a/beast/utility/empty_base_optimization.h b/beast/utility/empty_base_optimization.h index 726d67db61..2989f0987b 100644 --- a/beast/utility/empty_base_optimization.h +++ b/beast/utility/empty_base_optimization.h @@ -1,7 +1,8 @@ //------------------------------------------------------------------------------ /* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2014, Howard Hinnant , + Vinnie Falco Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/beast/utility/hardened_hash.h b/beast/utility/hardened_hash.h new file mode 100644 index 0000000000..6e1979609c --- /dev/null +++ b/beast/utility/hardened_hash.h @@ -0,0 +1,168 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_UTILITY_HARDENED_HASH_H_INCLUDED +#define BEAST_UTILITY_HARDENED_HASH_H_INCLUDED + +#include "is_call_possible.h" + +#include "noexcept.h" +#include +#include +#include +#include "../cxx14/utility.h" // + +// When set to 1, makes the seed per-process instead +// of per default-constructed instance of hardened_hash +// +#ifndef BEAST_NO_HARDENED_HASH_INSTANCE_SEED +# ifdef __GLIBCXX__ +# define BEAST_NO_HARDENED_HASH_INSTANCE_SEED 1 +# else +# define BEAST_NO_HARDENED_HASH_INSTANCE_SEED 0 +# endif +#endif + +namespace beast { + +namespace detail { + +template +class hardened_hash_base +{ +public: + typedef Result result_type; + +private: + static + result_type + next_seed() noexcept + { + static std::mutex mutex; + static std::random_device rng; + static std::mt19937_64 gen (rng()); + std::lock_guard lock (mutex); + std::uniform_int_distribution dist; + result_type value; + for(;;) + { + value = dist (gen); + // VFALCO Do we care if 0 is picked? + if (value != 0) + break; + } + return value; + } + +#if BEAST_NO_HARDENED_HASH_INSTANCE_SEED +protected: + result_type + seed() const noexcept + { + static result_type const value (next_seed()); + return value; + } + +#else +protected: + hardened_hash_base() noexcept + : m_seed (next_seed()) + { + } + + result_type + seed() const noexcept + { + return m_seed; + } + +private: + // VFALCO Should seed be per process or per hash function? + result_type m_seed; + +#endif +}; + +} + +/** A std compatible hash adapter that resists adversarial inputs. + For this to work, one of the following must exist: + + * A member function of `T` called `hash_combine` with + this signature: + + @code + + void hash_combine (std::size_t&) const noexcept; + + @endcode + + * A free function called `hash_combine`, found via argument + dependent lookup, callable with this signature: + + @code + + void hash_combine (std::size_t, T const& t) noexcept; + + @endcode +*/ +template +class hardened_hash + : public detail::hardened_hash_base +{ +public: + typedef T argument_type; + using detail::hardened_hash_base ::result_type; + +private: + BEAST_DEFINE_IS_CALL_POSSIBLE(has_hash_combine,hash_combine); + + typedef detail::hardened_hash_base base; + + // Called when hash_combine is a member function + result_type + operator() (argument_type const& key, std::true_type) const noexcept + { + result_type result (base::seed()); + key.hash_combine (result); + return result; + } + + result_type + operator() (argument_type const& key, std::false_type) const noexcept + { + result_type result (base::seed()); + hash_combine (result, key); + return result; + } + +public: + hardened_hash() = default; + + result_type + operator() (argument_type const& key) const noexcept + { + return operator() (key, std::integral_constant ::value>()); + } +}; + +} + +#endif diff --git a/beast/utility/is_call_possible.h b/beast/utility/is_call_possible.h index c38cfa86f8..099c9fccd1 100644 --- a/beast/utility/is_call_possible.h +++ b/beast/utility/is_call_possible.h @@ -29,25 +29,25 @@ namespace beast { // namespace is_call_possible_detail { - template + template struct add_reference { - typedef T& type; + typedef Z& type; }; - template - struct add_reference + template + struct add_reference { - typedef T& type; + typedef Z& type; }; - template class void_exp_result {}; + template class void_exp_result {}; - template - U const& operator,(U const&, void_exp_result); + template + U const& operator,(U const&, void_exp_result); - template - U& operator,(U&, void_exp_result); + template + U& operator,(U&, void_exp_result); template struct clone_constness @@ -63,10 +63,10 @@ namespace is_call_possible_detail } #define BEAST_DEFINE_HAS_MEMBER_FUNCTION(trait_name, member_function_name) \ -template class trait_name; \ +template class trait_name; \ \ -template \ -class trait_name \ +template \ +class trait_name \ { \ class yes { char m; }; \ class no { yes m[2]; }; \ @@ -74,7 +74,7 @@ class trait_name { \ Result member_function_name(); \ }; \ - struct base : public T, public base_mixin { private: base(); }; \ + struct base : public Z, public base_mixin { private: base(); }; \ template class helper{}; \ template \ static no deduce(U*, helper* = 0); \ @@ -83,8 +83,8 @@ public: static const bool value = sizeof(yes) == sizeof(deduce(static_cast(0))); \ }; \ \ -template \ -class trait_name \ +template \ +class trait_name \ { \ class yes { char m; }; \ class no { yes m[2]; }; \ @@ -92,7 +92,7 @@ class trait_name { \ Result member_function_name(Arg); \ }; \ - struct base : public T, public base_mixin { private: base(); }; \ + struct base : public Z, public base_mixin { private: base(); }; \ template class helper{}; \ template \ static no deduce(U*, helper* = 0); \ @@ -101,8 +101,8 @@ public: static const bool value = sizeof(yes) == sizeof(deduce(static_cast(0))); \ }; \ \ -template \ -class trait_name \ +template \ +class trait_name \ { \ class yes { char m; }; \ class no { yes m[2]; }; \ @@ -110,7 +110,7 @@ class trait_name { \ Result member_function_name(Arg1,Arg2); \ }; \ - struct base : public T, public base_mixin { private: base(); }; \ + struct base : public Z, public base_mixin { private: base(); }; \ template class helper{}; \ template \ static no deduce(U*, helper* = 0); \ @@ -119,8 +119,8 @@ public: static const bool value = sizeof(yes) == sizeof(deduce(static_cast(0))); \ }; \ \ -template \ -class trait_name \ +template \ +class trait_name \ { \ class yes { char m; }; \ class no { yes m[2]; }; \ @@ -128,7 +128,7 @@ class trait_name { \ Result member_function_name(Arg1,Arg2,Arg3); \ }; \ - struct base : public T, public base_mixin { private: base(); }; \ + struct base : public Z, public base_mixin { private: base(); }; \ template class helper{}; \ template \ static no deduce(U*, helper* = 0); \ @@ -137,8 +137,8 @@ public: static const bool value = sizeof(yes) == sizeof(deduce(static_cast(0))); \ }; \ \ -template \ -class trait_name \ +template \ +class trait_name \ { \ class yes { char m; }; \ class no { yes m[2]; }; \ @@ -146,7 +146,7 @@ class trait_name { \ Result member_function_name(Arg1,Arg2,Arg3,Arg4); \ }; \ - struct base : public T, public base_mixin { private: base(); }; \ + struct base : public Z, public base_mixin { private: base(); }; \ template class helper{}; \ template \ static no deduce(U*, helper* = 0); \ @@ -165,17 +165,17 @@ template struct trait_name \ { \ private: \ - typedef std::remove_reference_t
T; \ + typedef std::remove_reference_t
Z; \ class yes {}; \ class no { yes m[2]; }; \ - struct derived : public T \ + struct derived : public Z \ { \ - using T::member_function_name; \ + using Z::member_function_name; \ no member_function_name(...) const; \ private: derived (); \ }; \ \ - typedef typename beast::is_call_possible_detail::clone_constness::type derived_type; \ + typedef typename beast::is_call_possible_detail::clone_constness::type derived_type; \ \ template \ struct return_value_check \ @@ -183,7 +183,7 @@ private: static yes deduce(Result); \ static no deduce(...); \ static no deduce(no); \ - static no deduce(beast::is_call_possible_detail::void_exp_result); \ + static no deduce(beast::is_call_possible_detail::void_exp_result); \ }; \ \ template \ @@ -206,8 +206,8 @@ private: \ static const bool value = \ sizeof( \ - return_value_check::deduce( \ - (test_me.member_function_name(), beast::is_call_possible_detail::void_exp_result())) \ + return_value_check::deduce( \ + (test_me.member_function_name(), beast::is_call_possible_detail::void_exp_result())) \ ) == sizeof(yes); \ }; \ \ @@ -219,8 +219,8 @@ private: \ static const bool value = \ sizeof( \ - return_value_check::deduce( \ - (test_me.member_function_name(arg), beast::is_call_possible_detail::void_exp_result()) \ + return_value_check::deduce( \ + (test_me.member_function_name(arg), beast::is_call_possible_detail::void_exp_result()) \ ) \ ) == sizeof(yes); \ }; \ @@ -234,8 +234,8 @@ private: \ static const bool value = \ sizeof( \ - return_value_check::deduce( \ - (test_me.member_function_name(arg1,arg2), beast::is_call_possible_detail::void_exp_result()) \ + return_value_check::deduce( \ + (test_me.member_function_name(arg1,arg2), beast::is_call_possible_detail::void_exp_result()) \ ) \ ) == sizeof(yes); \ }; \ @@ -250,8 +250,8 @@ private: \ static const bool value = \ sizeof( \ - return_value_check::deduce( \ - (test_me.member_function_name(arg1,arg2,arg3), beast::is_call_possible_detail::void_exp_result()) \ + return_value_check::deduce( \ + (test_me.member_function_name(arg1,arg2,arg3), beast::is_call_possible_detail::void_exp_result()) \ ) \ ) == sizeof(yes); \ }; \ @@ -267,15 +267,15 @@ private: \ static const bool value = \ sizeof( \ - return_value_check::deduce( \ + return_value_check::deduce( \ (test_me.member_function_name(arg1,arg2,arg3,arg4), \ - beast::is_call_possible_detail::void_exp_result()) \ + beast::is_call_possible_detail::void_exp_result()) \ ) \ ) == sizeof(yes); \ }; \ \ public: \ - static const bool value = impl::value, \ + static const bool value = impl::value, \ IsCallPossibleSignature>::value; \ } diff --git a/beast/utility/tests/empty_base_optimization.test.cpp b/beast/utility/tests/empty_base_optimization.test.cpp index 57c57287aa..1a72c2aed0 100644 --- a/beast/utility/tests/empty_base_optimization.test.cpp +++ b/beast/utility/tests/empty_base_optimization.test.cpp @@ -1,7 +1,7 @@ -//-------------------- empty_base_optimization.test.cpp ------------------------ +//------------------------------------------------------------------------------ /* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2014 Ripple Labs Inc. + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2014, Howard Hinnant Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/beast/utility/tests/hardened_hash.test.cpp b/beast/utility/tests/hardened_hash.test.cpp new file mode 100644 index 0000000000..c7dcdedbab --- /dev/null +++ b/beast/utility/tests/hardened_hash.test.cpp @@ -0,0 +1,311 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#if BEAST_INCLUDE_BEASTCONFIG +#include "../../../BeastConfig.h" +#endif + +#include "../hardened_hash.h" +#include "../../unit_test/suite.h" + +#include "../../crypto/Sha256.h" +#include + +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace detail { + +template +class test_user_type_member +{ +private: + T t; + +public: + explicit test_user_type_member (T const& t_ = T()) + : t (t_) + { + } + + void + hash_combine (std::size_t& seed) const noexcept + { + boost::hash_combine (seed, t); + } +}; + +template +class test_user_type_free +{ +private: + T t; + +public: + explicit test_user_type_free (T const& t_ = T()) + : t (t_) + { + } + + friend + void + hash_combine (std::size_t& seed, + test_user_type_free const& v) noexcept + { + boost::hash_combine (seed, v.t); + } +}; + +} // detail +} // beast + +//------------------------------------------------------------------------------ + +namespace beast { + +namespace detail { + +template +using test_hardened_unordered_set = + std::unordered_set >; + +template +using test_hardened_unordered_map = + std::unordered_map >; + +template +using test_hardened_unordered_multiset = + std::unordered_multiset >; + +template +using test_hardened_unordered_multimap = + std::unordered_multimap >; + +} // beast + +template +class unsigned_integer +{ +private: + static_assert (std::is_integral::value && + std::is_unsigned ::value, + "UInt must be an unsigned integral type"); + + static_assert (Bits%(8*sizeof(UInt))==0, + "Bits must be a multiple of 8*sizeof(UInt)"); + + static_assert (Bits >= (8*sizeof(UInt)), + "Bits must be at least 8*sizeof(UInt)"); + + static std::size_t const size = Bits/(8*sizeof(UInt)); + + std::array m_vec; + +public: + typedef UInt value_type; + + static std::size_t const bits = Bits; + static std::size_t const bytes = bits / 8; + + template + static + unsigned_integer + from_number (Int v) + { + unsigned_integer result; + for (std::size_t i (1); i < size; ++i) + result.m_vec [i] = 0; + result.m_vec[0] = v; + return result; + } + + void* + data() noexcept + { + return &m_vec[0]; + } + + void const* + data() const noexcept + { + return &m_vec[0]; + } + + void + hash_combine (std::size_t& seed) const noexcept + { + for (std::size_t i (0); i < size; ++i) + boost::hash_combine (seed, m_vec[i]); + } + + friend + std::ostream& + operator<< (std::ostream& s, unsigned_integer const& v) + { + for (std::size_t i (0); i < size; ++i) + s << + std::hex << + std::setfill ('0') << + std::setw (2*sizeof(UInt)) << + v.m_vec[i] + ; + return s; + } +}; + +typedef unsigned_integer <256, std::size_t> sha256_t; + +static_assert (sha256_t::bits == 256, + "sha256_t must have 256 bits"); + +} // beast + +//------------------------------------------------------------------------------ + +namespace beast { + +class hardened_hash_test + : public unit_test::suite +{ +public: + template + void + check () + { + T t{}; + hardened_hash () (t); + pass(); + } + + template