diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/third_party/botan/src/lib/utils | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/third_party/botan/src/lib/utils')
98 files changed, 13873 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/utils/assert.cpp b/comm/third_party/botan/src/lib/utils/assert.cpp new file mode 100644 index 0000000000..b251a469e9 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/assert.cpp @@ -0,0 +1,54 @@ +/* +* Runtime assertion checking +* (C) 2010,2012,2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/exceptn.h> +#include <sstream> + +namespace Botan { + +void throw_invalid_argument(const char* message, + const char* func, + const char* file) + { + std::ostringstream format; + format << message << " in " << func << ":" << file; + throw Invalid_Argument(format.str()); + } + +void throw_invalid_state(const char* expr, + const char* func, + const char* file) + { + std::ostringstream format; + format << "Invalid state: " << expr << " was false in " << func << ":" << file; + throw Invalid_State(format.str()); + } + +void assertion_failure(const char* expr_str, + const char* assertion_made, + const char* func, + const char* file, + int line) + { + std::ostringstream format; + + format << "False assertion "; + + if(assertion_made && assertion_made[0] != 0) + format << "'" << assertion_made << "' (expression " << expr_str << ") "; + else + format << expr_str << " "; + + if(func) + format << "in " << func << " "; + + format << "@" << file << ":" << line; + + throw Internal_Error(format.str()); + } + +} diff --git a/comm/third_party/botan/src/lib/utils/assert.h b/comm/third_party/botan/src/lib/utils/assert.h new file mode 100644 index 0000000000..14cc442609 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/assert.h @@ -0,0 +1,157 @@ +/* +* Runtime assertion checking +* (C) 2010,2018 Jack Lloyd +* 2017 Simon Warta (Kullo GmbH) +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_ASSERTION_CHECKING_H_ +#define BOTAN_ASSERTION_CHECKING_H_ + +#include <botan/build.h> +#include <botan/compiler.h> + +namespace Botan { + +/** +* Called when an assertion fails +* Throws an Exception object +*/ +BOTAN_NORETURN void BOTAN_PUBLIC_API(2,0) + assertion_failure(const char* expr_str, + const char* assertion_made, + const char* func, + const char* file, + int line); + +/** +* Called when an invalid argument is used +* Throws Invalid_Argument +*/ +BOTAN_NORETURN void BOTAN_UNSTABLE_API throw_invalid_argument(const char* message, + const char* func, + const char* file); + + +#define BOTAN_ARG_CHECK(expr, msg) \ + do { if(!(expr)) Botan::throw_invalid_argument(msg, __func__, __FILE__); } while(0) + +/** +* Called when an invalid state is encountered +* Throws Invalid_State +*/ +BOTAN_NORETURN void BOTAN_UNSTABLE_API throw_invalid_state(const char* message, + const char* func, + const char* file); + + +#define BOTAN_STATE_CHECK(expr) \ + do { if(!(expr)) Botan::throw_invalid_state(#expr, __func__, __FILE__); } while(0) + +/** +* Make an assertion +*/ +#define BOTAN_ASSERT(expr, assertion_made) \ + do { \ + if(!(expr)) \ + Botan::assertion_failure(#expr, \ + assertion_made, \ + __func__, \ + __FILE__, \ + __LINE__); \ + } while(0) + +/** +* Make an assertion +*/ +#define BOTAN_ASSERT_NOMSG(expr) \ + do { \ + if(!(expr)) \ + Botan::assertion_failure(#expr, \ + "", \ + __func__, \ + __FILE__, \ + __LINE__); \ + } while(0) + +/** +* Assert that value1 == value2 +*/ +#define BOTAN_ASSERT_EQUAL(expr1, expr2, assertion_made) \ + do { \ + if((expr1) != (expr2)) \ + Botan::assertion_failure(#expr1 " == " #expr2, \ + assertion_made, \ + __func__, \ + __FILE__, \ + __LINE__); \ + } while(0) + +/** +* Assert that expr1 (if true) implies expr2 is also true +*/ +#define BOTAN_ASSERT_IMPLICATION(expr1, expr2, msg) \ + do { \ + if((expr1) && !(expr2)) \ + Botan::assertion_failure(#expr1 " implies " #expr2, \ + msg, \ + __func__, \ + __FILE__, \ + __LINE__); \ + } while(0) + +/** +* Assert that a pointer is not null +*/ +#define BOTAN_ASSERT_NONNULL(ptr) \ + do { \ + if((ptr) == nullptr) \ + Botan::assertion_failure(#ptr " is not null", \ + "", \ + __func__, \ + __FILE__, \ + __LINE__); \ + } while(0) + +#if defined(BOTAN_ENABLE_DEBUG_ASSERTS) + +#define BOTAN_DEBUG_ASSERT(expr) BOTAN_ASSERT_NOMSG(expr) + +#else + +#define BOTAN_DEBUG_ASSERT(expr) do {} while(0) + +#endif + +/** +* Mark variable as unused. Takes between 1 and 9 arguments and marks all as unused, +* e.g. BOTAN_UNUSED(a); or BOTAN_UNUSED(x, y, z); +*/ +#define _BOTAN_UNUSED_IMPL1(a) static_cast<void>(a) +#define _BOTAN_UNUSED_IMPL2(a, b) static_cast<void>(a); _BOTAN_UNUSED_IMPL1(b) +#define _BOTAN_UNUSED_IMPL3(a, b, c) static_cast<void>(a); _BOTAN_UNUSED_IMPL2(b, c) +#define _BOTAN_UNUSED_IMPL4(a, b, c, d) static_cast<void>(a); _BOTAN_UNUSED_IMPL3(b, c, d) +#define _BOTAN_UNUSED_IMPL5(a, b, c, d, e) static_cast<void>(a); _BOTAN_UNUSED_IMPL4(b, c, d, e) +#define _BOTAN_UNUSED_IMPL6(a, b, c, d, e, f) static_cast<void>(a); _BOTAN_UNUSED_IMPL5(b, c, d, e, f) +#define _BOTAN_UNUSED_IMPL7(a, b, c, d, e, f, g) static_cast<void>(a); _BOTAN_UNUSED_IMPL6(b, c, d, e, f, g) +#define _BOTAN_UNUSED_IMPL8(a, b, c, d, e, f, g, h) static_cast<void>(a); _BOTAN_UNUSED_IMPL7(b, c, d, e, f, g, h) +#define _BOTAN_UNUSED_IMPL9(a, b, c, d, e, f, g, h, i) static_cast<void>(a); _BOTAN_UNUSED_IMPL8(b, c, d, e, f, g, h, i) +#define _BOTAN_UNUSED_GET_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, IMPL_NAME, ...) IMPL_NAME + +#define BOTAN_UNUSED(...) _BOTAN_UNUSED_GET_IMPL(__VA_ARGS__, \ + _BOTAN_UNUSED_IMPL9, \ + _BOTAN_UNUSED_IMPL8, \ + _BOTAN_UNUSED_IMPL7, \ + _BOTAN_UNUSED_IMPL6, \ + _BOTAN_UNUSED_IMPL5, \ + _BOTAN_UNUSED_IMPL4, \ + _BOTAN_UNUSED_IMPL3, \ + _BOTAN_UNUSED_IMPL2, \ + _BOTAN_UNUSED_IMPL1, \ + unused dummy rest value \ + ) /* we got an one of _BOTAN_UNUSED_IMPL*, now call it */ (__VA_ARGS__) + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/bit_ops.h b/comm/third_party/botan/src/lib/utils/bit_ops.h new file mode 100644 index 0000000000..79d86f0f76 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/bit_ops.h @@ -0,0 +1,171 @@ +/* +* Bit/Word Operations +* (C) 1999-2008 Jack Lloyd +* (C) Copyright Projet SECRET, INRIA, Rocquencourt +* (C) Bhaskar Biswas and Nicolas Sendrier +* (C) 2014 cryptosource GmbH +* (C) 2014 Falko Strenzke fstrenzke@cryptosource.de +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_BIT_OPS_H_ +#define BOTAN_BIT_OPS_H_ + +#include <botan/types.h> + +namespace Botan { + +/** +* If top bit of arg is set, return ~0. Otherwise return 0. +*/ +template<typename T> +inline T expand_top_bit(T a) + { + return static_cast<T>(0) - (a >> (sizeof(T)*8-1)); + } + +/** +* If arg is zero, return ~0. Otherwise return 0 +*/ +template<typename T> +inline T ct_is_zero(T x) + { + return expand_top_bit<T>(~x & (x - 1)); + } + +/** +* Power of 2 test. T should be an unsigned integer type +* @param arg an integer value +* @return true iff arg is 2^n for some n > 0 +*/ +template<typename T> +inline constexpr bool is_power_of_2(T arg) + { + return (arg != 0) && (arg != 1) && ((arg & static_cast<T>(arg-1)) == 0); + } + +/** +* Return the index of the highest set bit +* T is an unsigned integer type +* @param n an integer value +* @return index of the highest set bit in n +*/ +template<typename T> +inline size_t high_bit(T n) + { + size_t hb = 0; + + for(size_t s = 8*sizeof(T) / 2; s > 0; s /= 2) + { + const size_t z = s * ((~ct_is_zero(n >> s)) & 1); + hb += z; + n >>= z; + } + + hb += n; + + return hb; + } + +/** +* Return the number of significant bytes in n +* @param n an integer value +* @return number of significant bytes in n +*/ +template<typename T> +inline size_t significant_bytes(T n) + { + size_t b = 0; + + for(size_t s = 8*sizeof(n) / 2; s >= 8; s /= 2) + { + const size_t z = s * (~ct_is_zero(n >> s) & 1); + b += z/8; + n >>= z; + } + + b += (n != 0); + + return b; + } + +/** +* Count the trailing zero bits in n +* @param n an integer value +* @return maximum x st 2^x divides n +*/ +template<typename T> +inline size_t ctz(T n) + { + /* + * If n == 0 then this function will compute 8*sizeof(T)-1, so + * initialize lb to 1 if n == 0 to produce the expected result. + */ + size_t lb = ct_is_zero(n) & 1; + + for(size_t s = 8*sizeof(T) / 2; s > 0; s /= 2) + { + const T mask = (static_cast<T>(1) << s) - 1; + const size_t z = s * (ct_is_zero(n & mask) & 1); + lb += z; + n >>= z; + } + + return lb; + } + +template<typename T> +uint8_t ceil_log2(T x) + { + static_assert(sizeof(T) < 32, "Abnormally large scalar"); + + if(x >> (sizeof(T)*8-1)) + return sizeof(T)*8; + + uint8_t result = 0; + T compare = 1; + + while(compare < x) + { + compare <<= 1; + result++; + } + + return result; + } + +// Potentially variable time ctz used for OCB +inline size_t var_ctz32(uint32_t n) + { +#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG) + if(n == 0) + return 32; + return __builtin_ctz(n); +#else + return ctz<uint32_t>(n); +#endif + } + +template<typename T> +inline T bit_permute_step(T x, T mask, size_t shift) + { + /* + See https://reflectionsonsecurity.wordpress.com/2014/05/11/efficient-bit-permutation-using-delta-swaps/ + and http://programming.sirrida.de/bit_perm.html + */ + const T swap = ((x >> shift) ^ x) & mask; + return (x ^ swap) ^ (swap << shift); + } + +template<typename T> +inline void swap_bits(T& x, T& y, T mask, size_t shift) + { + const T swap = ((x >> shift) ^ y) & mask; + x ^= swap << shift; + y ^= swap; + } + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/boost/info.txt b/comm/third_party/botan/src/lib/utils/boost/info.txt new file mode 100644 index 0000000000..6330cbafdb --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/boost/info.txt @@ -0,0 +1,9 @@ +<defines> +BOOST_ASIO -> 20131228 +</defines> + +load_on vendor + +<libs> +all -> boost_system +</libs> diff --git a/comm/third_party/botan/src/lib/utils/bswap.h b/comm/third_party/botan/src/lib/utils/bswap.h new file mode 100644 index 0000000000..584fa33234 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/bswap.h @@ -0,0 +1,108 @@ +/* +* Byte Swapping Operations +* (C) 1999-2011,2018 Jack Lloyd +* (C) 2007 Yves Jerschow +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_BYTE_SWAP_H_ +#define BOTAN_BYTE_SWAP_H_ + +#include <botan/types.h> + +#if defined(BOTAN_BUILD_COMPILER_IS_MSVC) + #include <stdlib.h> +#endif + +BOTAN_FUTURE_INTERNAL_HEADER(bswap.h) + +namespace Botan { + +/** +* Swap a 16 bit integer +*/ +inline uint16_t reverse_bytes(uint16_t val) + { +#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG) || defined(BOTAN_BUILD_COMPILER_IS_XLC) + return __builtin_bswap16(val); +#else + return static_cast<uint16_t>((val << 8) | (val >> 8)); +#endif + } + +/** +* Swap a 32 bit integer +*/ +inline uint32_t reverse_bytes(uint32_t val) + { +#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG) || defined(BOTAN_BUILD_COMPILER_IS_XLC) + return __builtin_bswap32(val); + +#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC) + return _byteswap_ulong(val); + +#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + + // GCC-style inline assembly for x86 or x86-64 + asm("bswapl %0" : "=r" (val) : "0" (val)); + return val; + +#else + // Generic implementation + uint16_t hi = static_cast<uint16_t>(val >> 16); + uint16_t lo = static_cast<uint16_t>(val); + + hi = reverse_bytes(hi); + lo = reverse_bytes(lo); + + return (static_cast<uint32_t>(lo) << 16) | hi; +#endif + } + +/** +* Swap a 64 bit integer +*/ +inline uint64_t reverse_bytes(uint64_t val) + { +#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG) || defined(BOTAN_BUILD_COMPILER_IS_XLC) + return __builtin_bswap64(val); + +#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC) + return _byteswap_uint64(val); + +#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_X86_64) + // GCC-style inline assembly for x86-64 + asm("bswapq %0" : "=r" (val) : "0" (val)); + return val; + +#else + /* Generic implementation. Defined in terms of 32-bit bswap so any + * optimizations in that version can help. + */ + + uint32_t hi = static_cast<uint32_t>(val >> 32); + uint32_t lo = static_cast<uint32_t>(val); + + hi = reverse_bytes(hi); + lo = reverse_bytes(lo); + + return (static_cast<uint64_t>(lo) << 32) | hi; +#endif + } + +/** +* Swap 4 Ts in an array +*/ +template<typename T> +inline void bswap_4(T x[4]) + { + x[0] = reverse_bytes(x[0]); + x[1] = reverse_bytes(x[1]); + x[2] = reverse_bytes(x[2]); + x[3] = reverse_bytes(x[3]); + } + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/calendar.cpp b/comm/third_party/botan/src/lib/utils/calendar.cpp new file mode 100644 index 0000000000..1c34a71572 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/calendar.cpp @@ -0,0 +1,124 @@ +/* +* Calendar Functions +* (C) 1999-2010,2017 Jack Lloyd +* (C) 2015 Simon Warta (Kullo GmbH) +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/calendar.h> +#include <botan/exceptn.h> +#include <ctime> +#include <sstream> +#include <iomanip> +#include <stdlib.h> + +namespace Botan { + +namespace { + +std::tm do_gmtime(std::time_t time_val) + { + std::tm tm; + +#if defined(BOTAN_TARGET_OS_HAS_WIN32) + ::gmtime_s(&tm, &time_val); // Windows +#elif defined(BOTAN_TARGET_OS_HAS_POSIX1) + ::gmtime_r(&time_val, &tm); // Unix/SUSv2 +#else + std::tm* tm_p = std::gmtime(&time_val); + if (tm_p == nullptr) + throw Encoding_Error("time_t_to_tm could not convert"); + tm = *tm_p; +#endif + + return tm; + } + +/* +Portable replacement for timegm, _mkgmtime, etc + +Algorithm due to Howard Hinnant + +See https://howardhinnant.github.io/date_algorithms.html#days_from_civil +for details and explaination. The code is slightly simplified by our assumption +that the date is at least 1970, which is sufficient for our purposes. +*/ +size_t days_since_epoch(uint32_t year, uint32_t month, uint32_t day) + { + if(month <= 2) + year -= 1; + const uint32_t era = year / 400; + const uint32_t yoe = year - era * 400; // [0, 399] + const uint32_t doy = (153*(month + (month > 2 ? -3 : 9)) + 2)/5 + day-1; // [0, 365] + const uint32_t doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096] + return era * 146097 + doe - 719468; + } + +} + +std::chrono::system_clock::time_point calendar_point::to_std_timepoint() const + { + if(get_year() < 1970) + throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years before 1970"); + + // 32 bit time_t ends at January 19, 2038 + // https://msdn.microsoft.com/en-us/library/2093ets1.aspx + // Throw after 2037 if 32 bit time_t is used + + BOTAN_IF_CONSTEXPR(sizeof(std::time_t) == 4) + { + if(get_year() > 2037) + { + throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2037 on this system"); + } + } + + // This upper bound is completely arbitrary + if(get_year() >= 2400) + { + throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2400"); + } + + const uint64_t seconds_64 = (days_since_epoch(get_year(), get_month(), get_day()) * 86400) + + (get_hour() * 60 * 60) + (get_minutes() * 60) + get_seconds(); + + const time_t seconds_time_t = static_cast<time_t>(seconds_64); + + if(seconds_64 - seconds_time_t != 0) + { + throw Invalid_Argument("calendar_point::to_std_timepoint time_t overflow"); + } + + return std::chrono::system_clock::from_time_t(seconds_time_t); + } + +std::string calendar_point::to_string() const + { + // desired format: <YYYY>-<MM>-<dd>T<HH>:<mm>:<ss> + std::stringstream output; + output << std::setfill('0') + << std::setw(4) << get_year() << "-" + << std::setw(2) << get_month() << "-" + << std::setw(2) << get_day() << "T" + << std::setw(2) << get_hour() << ":" + << std::setw(2) << get_minutes() << ":" + << std::setw(2) << get_seconds(); + return output.str(); + } + + +calendar_point calendar_value( + const std::chrono::system_clock::time_point& time_point) + { + std::tm tm = do_gmtime(std::chrono::system_clock::to_time_t(time_point)); + + return calendar_point(tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec); + } + +} diff --git a/comm/third_party/botan/src/lib/utils/calendar.h b/comm/third_party/botan/src/lib/utils/calendar.h new file mode 100644 index 0000000000..83759070b8 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/calendar.h @@ -0,0 +1,91 @@ +/* +* Calendar Functions +* (C) 1999-2009,2015 Jack Lloyd +* (C) 2015 Simon Warta (Kullo GmbH) +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CALENDAR_H_ +#define BOTAN_CALENDAR_H_ + +#include <botan/types.h> +#include <chrono> +#include <string> + +namespace Botan { + +/** +* Struct representing a particular date and time +*/ +class BOTAN_PUBLIC_API(2,0) calendar_point + { + public: + + /** The year */ + uint32_t get_year() const { return year; } + + /** The month, 1 through 12 for Jan to Dec */ + uint32_t get_month() const { return month; } + + /** The day of the month, 1 through 31 (or 28 or 30 based on month */ + uint32_t get_day() const { return day; } + + /** Hour in 24-hour form, 0 to 23 */ + uint32_t get_hour() const { return hour; } + + /** Minutes in the hour, 0 to 60 */ + uint32_t get_minutes() const { return minutes; } + + /** Seconds in the minute, 0 to 60, but might be slightly + larger to deal with leap seconds on some systems + */ + uint32_t get_seconds() const { return seconds; } + + /** + * Initialize a calendar_point + * @param y the year + * @param mon the month + * @param d the day + * @param h the hour + * @param min the minute + * @param sec the second + */ + calendar_point(uint32_t y, uint32_t mon, uint32_t d, uint32_t h, uint32_t min, uint32_t sec) : + year(y), month(mon), day(d), hour(h), minutes(min), seconds(sec) {} + + /** + * Returns an STL timepoint object + */ + std::chrono::system_clock::time_point to_std_timepoint() const; + + /** + * Returns a human readable string of the struct's components. + * Formatting might change over time. Currently it is RFC339 'iso-date-time'. + */ + std::string to_string() const; + + BOTAN_DEPRECATED_PUBLIC_MEMBER_VARIABLES: + /* + The member variables are public for historical reasons. Use the get_xxx() functions + defined above. These members will be made private in a future major release. + */ + uint32_t year; + uint32_t month; + uint32_t day; + uint32_t hour; + uint32_t minutes; + uint32_t seconds; + }; + +/** +* Convert a time_point to a calendar_point +* @param time_point a time point from the system clock +* @return calendar_point object representing this time point +*/ +BOTAN_PUBLIC_API(2,0) calendar_point calendar_value( + const std::chrono::system_clock::time_point& time_point); + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/charset.cpp b/comm/third_party/botan/src/lib/utils/charset.cpp new file mode 100644 index 0000000000..ca32c652db --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/charset.cpp @@ -0,0 +1,283 @@ +/* +* Character Set Handling +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/charset.h> +#include <botan/exceptn.h> +#include <botan/loadstor.h> +#include <cctype> + +namespace Botan { + +namespace { + +void append_utf8_for(std::string& s, uint32_t c) + { + if(c >= 0xD800 && c < 0xE000) + throw Decoding_Error("Invalid Unicode character"); + + if(c <= 0x7F) + { + const uint8_t b0 = static_cast<uint8_t>(c); + s.push_back(static_cast<char>(b0)); + } + else if(c <= 0x7FF) + { + const uint8_t b0 = 0xC0 | static_cast<uint8_t>(c >> 6); + const uint8_t b1 = 0x80 | static_cast<uint8_t>(c & 0x3F); + s.push_back(static_cast<char>(b0)); + s.push_back(static_cast<char>(b1)); + } + else if(c <= 0xFFFF) + { + const uint8_t b0 = 0xE0 | static_cast<uint8_t>(c >> 12); + const uint8_t b1 = 0x80 | static_cast<uint8_t>((c >> 6) & 0x3F); + const uint8_t b2 = 0x80 | static_cast<uint8_t>(c & 0x3F); + s.push_back(static_cast<char>(b0)); + s.push_back(static_cast<char>(b1)); + s.push_back(static_cast<char>(b2)); + } + else if(c <= 0x10FFFF) + { + const uint8_t b0 = 0xF0 | static_cast<uint8_t>(c >> 18); + const uint8_t b1 = 0x80 | static_cast<uint8_t>((c >> 12) & 0x3F); + const uint8_t b2 = 0x80 | static_cast<uint8_t>((c >> 6) & 0x3F); + const uint8_t b3 = 0x80 | static_cast<uint8_t>(c & 0x3F); + s.push_back(static_cast<char>(b0)); + s.push_back(static_cast<char>(b1)); + s.push_back(static_cast<char>(b2)); + s.push_back(static_cast<char>(b3)); + } + else + throw Decoding_Error("Invalid Unicode character"); + + } + +} + +std::string ucs2_to_utf8(const uint8_t ucs2[], size_t len) + { + if(len % 2 != 0) + throw Decoding_Error("Invalid length for UCS-2 string"); + + const size_t chars = len / 2; + + std::string s; + for(size_t i = 0; i != chars; ++i) + { + const uint16_t c = load_be<uint16_t>(ucs2, i); + append_utf8_for(s, c); + } + + return s; + } + +std::string ucs4_to_utf8(const uint8_t ucs4[], size_t len) + { + if(len % 4 != 0) + throw Decoding_Error("Invalid length for UCS-4 string"); + + const size_t chars = len / 4; + + std::string s; + for(size_t i = 0; i != chars; ++i) + { + const uint32_t c = load_be<uint32_t>(ucs4, i); + append_utf8_for(s, c); + } + + return s; + } + +/* +* Convert from UTF-8 to ISO 8859-1 +*/ +std::string utf8_to_latin1(const std::string& utf8) + { + std::string iso8859; + + size_t position = 0; + while(position != utf8.size()) + { + const uint8_t c1 = static_cast<uint8_t>(utf8[position++]); + + if(c1 <= 0x7F) + { + iso8859 += static_cast<char>(c1); + } + else if(c1 >= 0xC0 && c1 <= 0xC7) + { + if(position == utf8.size()) + throw Decoding_Error("UTF-8: sequence truncated"); + + const uint8_t c2 = static_cast<uint8_t>(utf8[position++]); + const uint8_t iso_char = ((c1 & 0x07) << 6) | (c2 & 0x3F); + + if(iso_char <= 0x7F) + throw Decoding_Error("UTF-8: sequence longer than needed"); + + iso8859 += static_cast<char>(iso_char); + } + else + throw Decoding_Error("UTF-8: Unicode chars not in Latin1 used"); + } + + return iso8859; + } + +namespace Charset { + +namespace { + +/* +* Convert from UCS-2 to ISO 8859-1 +*/ +std::string ucs2_to_latin1(const std::string& ucs2) + { + if(ucs2.size() % 2 == 1) + throw Decoding_Error("UCS-2 string has an odd number of bytes"); + + std::string latin1; + + for(size_t i = 0; i != ucs2.size(); i += 2) + { + const uint8_t c1 = ucs2[i]; + const uint8_t c2 = ucs2[i+1]; + + if(c1 != 0) + throw Decoding_Error("UCS-2 has non-Latin1 characters"); + + latin1 += static_cast<char>(c2); + } + + return latin1; + } + +/* +* Convert from ISO 8859-1 to UTF-8 +*/ +std::string latin1_to_utf8(const std::string& iso8859) + { + std::string utf8; + for(size_t i = 0; i != iso8859.size(); ++i) + { + const uint8_t c = static_cast<uint8_t>(iso8859[i]); + + if(c <= 0x7F) + utf8 += static_cast<char>(c); + else + { + utf8 += static_cast<char>((0xC0 | (c >> 6))); + utf8 += static_cast<char>((0x80 | (c & 0x3F))); + } + } + return utf8; + } + +} + +/* +* Perform character set transcoding +*/ +std::string transcode(const std::string& str, + Character_Set to, Character_Set from) + { + if(to == LOCAL_CHARSET) + to = LATIN1_CHARSET; + if(from == LOCAL_CHARSET) + from = LATIN1_CHARSET; + + if(to == from) + return str; + + if(from == LATIN1_CHARSET && to == UTF8_CHARSET) + return latin1_to_utf8(str); + if(from == UTF8_CHARSET && to == LATIN1_CHARSET) + return utf8_to_latin1(str); + if(from == UCS2_CHARSET && to == LATIN1_CHARSET) + return ucs2_to_latin1(str); + + throw Invalid_Argument("Unknown transcoding operation from " + + std::to_string(from) + " to " + std::to_string(to)); + } + +/* +* Check if a character represents a digit +*/ +bool is_digit(char c) + { + if(c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || + c == '5' || c == '6' || c == '7' || c == '8' || c == '9') + return true; + return false; + } + +/* +* Check if a character represents whitespace +*/ +bool is_space(char c) + { + if(c == ' ' || c == '\t' || c == '\n' || c == '\r') + return true; + return false; + } + +/* +* Convert a character to a digit +*/ +uint8_t char2digit(char c) + { + switch(c) + { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + } + + throw Invalid_Argument("char2digit: Input is not a digit character"); + } + +/* +* Convert a digit to a character +*/ +char digit2char(uint8_t b) + { + switch(b) + { + case 0: return '0'; + case 1: return '1'; + case 2: return '2'; + case 3: return '3'; + case 4: return '4'; + case 5: return '5'; + case 6: return '6'; + case 7: return '7'; + case 8: return '8'; + case 9: return '9'; + } + + throw Invalid_Argument("digit2char: Input is not a digit"); + } + +/* +* Case-insensitive character comparison +*/ +bool caseless_cmp(char a, char b) + { + return (std::tolower(static_cast<unsigned char>(a)) == + std::tolower(static_cast<unsigned char>(b))); + } + +} + +} diff --git a/comm/third_party/botan/src/lib/utils/charset.h b/comm/third_party/botan/src/lib/utils/charset.h new file mode 100644 index 0000000000..6e7ce30c91 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/charset.h @@ -0,0 +1,80 @@ +/* +* Character Set Handling +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CHARSET_H_ +#define BOTAN_CHARSET_H_ + +#include <botan/types.h> +#include <string> + +BOTAN_FUTURE_INTERNAL_HEADER(charset.h) + +namespace Botan { + +/** +* Convert a sequence of UCS-2 (big endian) characters to a UTF-8 string +* This is used for ASN.1 BMPString type +* @param ucs2 the sequence of UCS-2 characters +* @param len length of ucs2 in bytes, must be a multiple of 2 +*/ +std::string BOTAN_UNSTABLE_API ucs2_to_utf8(const uint8_t ucs2[], size_t len); + +/** +* Convert a sequence of UCS-4 (big endian) characters to a UTF-8 string +* This is used for ASN.1 UniversalString type +* @param ucs4 the sequence of UCS-4 characters +* @param len length of ucs4 in bytes, must be a multiple of 4 +*/ +std::string BOTAN_UNSTABLE_API ucs4_to_utf8(const uint8_t ucs4[], size_t len); + +/** +* Convert a UTF-8 string to Latin-1 +* If a character outside the Latin-1 range is encountered, an exception is thrown. +*/ +std::string BOTAN_UNSTABLE_API utf8_to_latin1(const std::string& utf8); + +/** +* The different charsets (nominally) supported by Botan. +*/ +enum Character_Set { + LOCAL_CHARSET, + UCS2_CHARSET, + UTF8_CHARSET, + LATIN1_CHARSET +}; + +namespace Charset { + +/* +* Character set conversion - avoid this. +* For specific conversions, use the functions above like +* ucs2_to_utf8 and utf8_to_latin1 +* +* If you need something more complex than that, use a real library +* such as iconv, Boost.Locale, or ICU +*/ +std::string BOTAN_PUBLIC_API(2,0) + BOTAN_DEPRECATED("Avoid. See comment in header.") + transcode(const std::string& str, + Character_Set to, + Character_Set from); + +/* +* Simple character classifier functions +*/ +bool BOTAN_PUBLIC_API(2,0) is_digit(char c); +bool BOTAN_PUBLIC_API(2,0) is_space(char c); +bool BOTAN_PUBLIC_API(2,0) caseless_cmp(char x, char y); + +uint8_t BOTAN_PUBLIC_API(2,0) char2digit(char c); +char BOTAN_PUBLIC_API(2,0) digit2char(uint8_t b); + +} + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/codec_base.h b/comm/third_party/botan/src/lib/utils/codec_base.h new file mode 100644 index 0000000000..2338fdfd7e --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/codec_base.h @@ -0,0 +1,220 @@ +/* +* Base Encoding and Decoding +* (C) 2018 Erwan Chaussy +* (C) 2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_BASE_CODEC_H_ +#define BOTAN_BASE_CODEC_H_ + +#include <botan/secmem.h> +#include <botan/exceptn.h> +#include <vector> +#include <string> + +namespace Botan { + +/** +* Perform encoding using the base provided +* @param base object giving access to the encodings specifications +* @param output an array of at least base.encode_max_output bytes +* @param input is some binary data +* @param input_length length of input in bytes +* @param input_consumed is an output parameter which says how many +* bytes of input were actually consumed. If less than +* input_length, then the range input[consumed:length] +* should be passed in later along with more input. +* @param final_inputs true iff this is the last input, in which case + padding chars will be applied if needed +* @return number of bytes written to output +*/ +template <class Base> +size_t base_encode(Base&& base, + char output[], + const uint8_t input[], + size_t input_length, + size_t& input_consumed, + bool final_inputs) + { + input_consumed = 0; + + const size_t encoding_bytes_in = base.encoding_bytes_in(); + const size_t encoding_bytes_out = base.encoding_bytes_out(); + + size_t input_remaining = input_length; + size_t output_produced = 0; + + while(input_remaining >= encoding_bytes_in) + { + base.encode(output + output_produced, input + input_consumed); + + input_consumed += encoding_bytes_in; + output_produced += encoding_bytes_out; + input_remaining -= encoding_bytes_in; + } + + if(final_inputs && input_remaining) + { + std::vector<uint8_t> remainder(encoding_bytes_in, 0); + for(size_t i = 0; i != input_remaining; ++i) + { remainder[i] = input[input_consumed + i]; } + + base.encode(output + output_produced, remainder.data()); + + const size_t bits_consumed = base.bits_consumed(); + const size_t remaining_bits_before_padding = base.remaining_bits_before_padding(); + + size_t empty_bits = 8 * (encoding_bytes_in - input_remaining); + size_t index = output_produced + encoding_bytes_out - 1; + while(empty_bits >= remaining_bits_before_padding) + { + output[index--] = '='; + empty_bits -= bits_consumed; + } + + input_consumed += input_remaining; + output_produced += encoding_bytes_out; + } + + return output_produced; + } + + +template <typename Base> +std::string base_encode_to_string(Base&& base, const uint8_t input[], size_t input_length) + { + const size_t output_length = base.encode_max_output(input_length); + std::string output(output_length, 0); + + size_t consumed = 0; + size_t produced = 0; + + if(output_length > 0) + { + produced = base_encode(base, &output.front(), + input, input_length, + consumed, true); + } + + BOTAN_ASSERT_EQUAL(consumed, input_length, "Consumed the entire input"); + BOTAN_ASSERT_EQUAL(produced, output.size(), "Produced expected size"); + + return output; + } + +/** +* Perform decoding using the base provided +* @param base object giving access to the encodings specifications +* @param output an array of at least Base::decode_max_output bytes +* @param input some base input +* @param input_length length of input in bytes +* @param input_consumed is an output parameter which says how many +* bytes of input were actually consumed. If less than +* input_length, then the range input[consumed:length] +* should be passed in later along with more input. +* @param final_inputs true iff this is the last input, in which case + padding is allowed +* @param ignore_ws ignore whitespace on input; if false, throw an + exception if whitespace is encountered +* @return number of bytes written to output +*/ +template <typename Base> +size_t base_decode(Base&& base, + uint8_t output[], + const char input[], + size_t input_length, + size_t& input_consumed, + bool final_inputs, + bool ignore_ws = true) + { + const size_t decoding_bytes_in = base.decoding_bytes_in(); + const size_t decoding_bytes_out = base.decoding_bytes_out(); + + uint8_t* out_ptr = output; + std::vector<uint8_t> decode_buf(decoding_bytes_in, 0); + size_t decode_buf_pos = 0; + size_t final_truncate = 0; + + clear_mem(output, base.decode_max_output(input_length)); + + for(size_t i = 0; i != input_length; ++i) + { + const uint8_t bin = base.lookup_binary_value(input[i]); + + if(base.check_bad_char(bin, input[i], ignore_ws)) // May throw Invalid_Argument + { + decode_buf[decode_buf_pos] = bin; + ++decode_buf_pos; + } + + /* + * If we're at the end of the input, pad with 0s and truncate + */ + if(final_inputs && (i == input_length - 1)) + { + if(decode_buf_pos) + { + for(size_t j = decode_buf_pos; j < decoding_bytes_in; ++j) + { decode_buf[j] = 0; } + + final_truncate = decoding_bytes_in - decode_buf_pos; + decode_buf_pos = decoding_bytes_in; + } + } + + if(decode_buf_pos == decoding_bytes_in) + { + base.decode(out_ptr, decode_buf.data()); + + out_ptr += decoding_bytes_out; + decode_buf_pos = 0; + input_consumed = i+1; + } + } + + while(input_consumed < input_length && + base.lookup_binary_value(input[input_consumed]) == 0x80) + { + ++input_consumed; + } + + size_t written = (out_ptr - output) - base.bytes_to_remove(final_truncate); + + return written; + } + +template<typename Base> +size_t base_decode_full(Base&& base, uint8_t output[], const char input[], size_t input_length, bool ignore_ws) + { + size_t consumed = 0; + const size_t written = base_decode(base, output, input, input_length, consumed, true, ignore_ws); + + if(consumed != input_length) + { + throw Invalid_Argument(base.name() + " decoding failed, input did not have full bytes"); + } + + return written; + } + +template<typename Vector, typename Base> +Vector base_decode_to_vec(Base&& base, + const char input[], + size_t input_length, + bool ignore_ws) + { + const size_t output_length = base.decode_max_output(input_length); + Vector bin(output_length); + + const size_t written = + base_decode_full(base, bin.data(), input, input_length, ignore_ws); + + bin.resize(written); + return bin; + } + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/compiler.h b/comm/third_party/botan/src/lib/utils/compiler.h new file mode 100644 index 0000000000..832ab8f22c --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/compiler.h @@ -0,0 +1,225 @@ +/* +* Define useful compiler-specific macros +* (C) 2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +/* This header is included in both C++ and C (via ffi.h) and should only + contain macro definitions. Avoid C++ style // comments in this file. +*/ + +#ifndef BOTAN_UTIL_COMPILER_FLAGS_H_ +#define BOTAN_UTIL_COMPILER_FLAGS_H_ + +/* Should we use GCC-style inline assembler? */ +#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || \ + defined(BOTAN_BUILD_COMPILER_IS_CLANG) || \ + defined(BOTAN_BUILD_COMPILER_IS_XLC) || \ + defined(BOTAN_BUILD_COMPILER_IS_SUN_STUDIO) + + #define BOTAN_USE_GCC_INLINE_ASM +#endif + +/** +* Used to annotate API exports which are public and supported. +* These APIs will not be broken/removed unless strictly required for +* functionality or security, and only in new major versions. +* @param maj The major version this public API was released in +* @param min The minor version this public API was released in +*/ +#define BOTAN_PUBLIC_API(maj,min) BOTAN_DLL + +/** +* Used to annotate API exports which are public, but are now deprecated +* and which will be removed in a future major release. +*/ +#define BOTAN_DEPRECATED_API(msg) BOTAN_DLL BOTAN_DEPRECATED(msg) + +/** +* Used to annotate API exports which are public and can be used by +* applications if needed, but which are intentionally not documented, +* and which may change incompatibly in a future major version. +*/ +#define BOTAN_UNSTABLE_API BOTAN_DLL + +/** +* Used to annotate API exports which are exported but only for the +* purposes of testing. They should not be used by applications and +* may be removed or changed without notice. +*/ +#define BOTAN_TEST_API BOTAN_DLL + +/* +* Define BOTAN_GCC_VERSION +*/ +#if defined(__GNUC__) && !defined(__clang__) + #define BOTAN_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ * 10 + __GNUC_PATCHLEVEL__) +#else + #define BOTAN_GCC_VERSION 0 +#endif + +/* +* Define BOTAN_CLANG_VERSION +*/ +#if defined(__clang__) + #define BOTAN_CLANG_VERSION (__clang_major__ * 10 + __clang_minor__) +#else + #define BOTAN_CLANG_VERSION 0 +#endif + +/* +* Define BOTAN_FUNC_ISA +*/ +#if (defined(__GNUC__) && !defined(__clang__)) || (BOTAN_CLANG_VERSION > 38) + #define BOTAN_FUNC_ISA(isa) __attribute__ ((target(isa))) +#else + #define BOTAN_FUNC_ISA(isa) +#endif + +/* +* Define BOTAN_WARN_UNUSED_RESULT +*/ +#if defined(__GNUC__) || defined(__clang__) + #define BOTAN_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) +#else + #define BOTAN_WARN_UNUSED_RESULT +#endif + +/* +* Define BOTAN_MALLOC_FN +*/ +#if defined(__ibmxl__) + /* XLC pretends to be both Clang and GCC, but is neither */ + #define BOTAN_MALLOC_FN __attribute__ ((malloc)) +#elif defined(__GNUC__) + #define BOTAN_MALLOC_FN __attribute__ ((malloc, alloc_size(1,2))) +#elif defined(_MSC_VER) + #define BOTAN_MALLOC_FN __declspec(restrict) +#else + #define BOTAN_MALLOC_FN +#endif + +/* +* Define BOTAN_DEPRECATED +*/ +#if !defined(BOTAN_NO_DEPRECATED_WARNINGS) && !defined(BOTAN_IS_BEING_BUILT) && !defined(BOTAN_AMALGAMATION_H_) + + #if defined(__clang__) + #define BOTAN_DEPRECATED(msg) __attribute__ ((deprecated(msg))) + #define BOTAN_DEPRECATED_HEADER(hdr) _Pragma("message \"this header is deprecated\"") + #define BOTAN_FUTURE_INTERNAL_HEADER(hdr) _Pragma("message \"this header will be made internal in the future\"") + + #elif defined(_MSC_VER) + #define BOTAN_DEPRECATED(msg) __declspec(deprecated(msg)) + #define BOTAN_DEPRECATED_HEADER(hdr) __pragma(message("this header is deprecated")) + #define BOTAN_FUTURE_INTERNAL_HEADER(hdr) __pragma(message("this header will be made internal in the future")) + + #elif defined(__GNUC__) + /* msg supported since GCC 4.5, earliest we support is 4.8 */ + #define BOTAN_DEPRECATED(msg) __attribute__ ((deprecated(msg))) + #define BOTAN_DEPRECATED_HEADER(hdr) _Pragma("GCC warning \"this header is deprecated\"") + #define BOTAN_FUTURE_INTERNAL_HEADER(hdr) _Pragma("GCC warning \"this header will be made internal in the future\"") + #endif + +#endif + +#if !defined(BOTAN_DEPRECATED) + #define BOTAN_DEPRECATED(msg) +#endif + +#if !defined(BOTAN_DEPRECATED_HEADER) + #define BOTAN_DEPRECATED_HEADER(hdr) +#endif + +#if !defined(BOTAN_FUTURE_INTERNAL_HEADER) + #define BOTAN_FUTURE_INTERNAL_HEADER(hdr) +#endif + +/* +* Define BOTAN_NORETURN +*/ +#if !defined(BOTAN_NORETURN) + + #if defined (__clang__) || defined (__GNUC__) + #define BOTAN_NORETURN __attribute__ ((__noreturn__)) + + #elif defined (_MSC_VER) + #define BOTAN_NORETURN __declspec(noreturn) + + #else + #define BOTAN_NORETURN + #endif + +#endif + +/* +* Define BOTAN_THREAD_LOCAL +*/ +#if !defined(BOTAN_THREAD_LOCAL) + + #if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_TARGET_OS_HAS_THREAD_LOCAL) + #define BOTAN_THREAD_LOCAL thread_local + #else + #define BOTAN_THREAD_LOCAL /**/ + #endif + +#endif + +/* +* Define BOTAN_IF_CONSTEXPR +*/ +#if !defined(BOTAN_IF_CONSTEXPR) + #if __cplusplus >= 201703 + #define BOTAN_IF_CONSTEXPR if constexpr + #else + #define BOTAN_IF_CONSTEXPR if + #endif +#endif + +/* +* Define BOTAN_PARALLEL_FOR +*/ +#if !defined(BOTAN_PARALLEL_FOR) + +#if defined(BOTAN_TARGET_HAS_OPENMP) + #define BOTAN_PARALLEL_FOR _Pragma("omp parallel for") for +#else + #define BOTAN_PARALLEL_FOR for +#endif + +#endif + +/* +* Define BOTAN_FORCE_INLINE +*/ +#if !defined(BOTAN_FORCE_INLINE) + + #if defined (__clang__) || defined (__GNUC__) + #define BOTAN_FORCE_INLINE __attribute__ ((__always_inline__)) inline + + #elif defined (_MSC_VER) + #define BOTAN_FORCE_INLINE __forceinline + + #else + #define BOTAN_FORCE_INLINE inline + #endif + +#endif + +/* +* Define BOTAN_PARALLEL_SIMD_FOR +*/ +#if !defined(BOTAN_PARALLEL_SIMD_FOR) + +#if defined(BOTAN_TARGET_HAS_OPENMP) + #define BOTAN_PARALLEL_SIMD_FOR _Pragma("omp simd") for +#elif defined(BOTAN_BUILD_COMPILER_IS_GCC) && (BOTAN_GCC_VERSION >= 490) + #define BOTAN_PARALLEL_SIMD_FOR _Pragma("GCC ivdep") for +#else + #define BOTAN_PARALLEL_SIMD_FOR for +#endif + +#endif + +#endif diff --git a/comm/third_party/botan/src/lib/utils/cpuid/cpuid.cpp b/comm/third_party/botan/src/lib/utils/cpuid/cpuid.cpp new file mode 100644 index 0000000000..e76e12ea8d --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/cpuid/cpuid.cpp @@ -0,0 +1,231 @@ +/* +* Runtime CPU detection +* (C) 2009,2010,2013,2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/cpuid.h> +#include <botan/types.h> +#include <botan/exceptn.h> +#include <botan/parsing.h> +#include <ostream> + +namespace Botan { + +bool CPUID::has_simd_32() + { +#if defined(BOTAN_TARGET_SUPPORTS_SSE2) + return CPUID::has_sse2(); +#elif defined(BOTAN_TARGET_SUPPORTS_ALTIVEC) + return CPUID::has_altivec(); +#elif defined(BOTAN_TARGET_SUPPORTS_NEON) + return CPUID::has_neon(); +#else + return true; +#endif + } + +//static +std::string CPUID::to_string() + { + std::vector<std::string> flags; + +#define CPUID_PRINT(flag) do { if(has_##flag()) { flags.push_back(#flag); } } while(0) + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + CPUID_PRINT(sse2); + CPUID_PRINT(ssse3); + CPUID_PRINT(sse41); + CPUID_PRINT(sse42); + CPUID_PRINT(avx2); + CPUID_PRINT(avx512f); + CPUID_PRINT(avx512dq); + CPUID_PRINT(avx512bw); + CPUID_PRINT(avx512_icelake); + + CPUID_PRINT(rdtsc); + CPUID_PRINT(bmi1); + CPUID_PRINT(bmi2); + CPUID_PRINT(adx); + + CPUID_PRINT(aes_ni); + CPUID_PRINT(clmul); + CPUID_PRINT(rdrand); + CPUID_PRINT(rdseed); + CPUID_PRINT(intel_sha); + CPUID_PRINT(avx512_aes); + CPUID_PRINT(avx512_clmul); +#endif + +#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + CPUID_PRINT(altivec); + CPUID_PRINT(power_crypto); + CPUID_PRINT(darn_rng); +#endif + +#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) + CPUID_PRINT(neon); + CPUID_PRINT(arm_sve); + + CPUID_PRINT(arm_sha1); + CPUID_PRINT(arm_sha2); + CPUID_PRINT(arm_aes); + CPUID_PRINT(arm_pmull); + CPUID_PRINT(arm_sha2_512); + CPUID_PRINT(arm_sha3); + CPUID_PRINT(arm_sm3); + CPUID_PRINT(arm_sm4); +#endif + +#undef CPUID_PRINT + + return string_join(flags, ' '); + } + +//static +void CPUID::print(std::ostream& o) + { + o << "CPUID flags: " << CPUID::to_string() << "\n"; + } + +//static +void CPUID::initialize() + { + state() = CPUID_Data(); + } + +CPUID::CPUID_Data::CPUID_Data() + { + m_cache_line_size = 0; + m_processor_features = 0; + +#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \ + defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \ + defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + + m_processor_features = detect_cpu_features(&m_cache_line_size); + +#endif + + m_processor_features |= CPUID::CPUID_INITIALIZED_BIT; + + if(m_cache_line_size == 0) + m_cache_line_size = BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE; + + m_endian_status = runtime_check_endian(); + } + +//static +CPUID::Endian_Status CPUID::CPUID_Data::runtime_check_endian() + { + // Check runtime endian + const uint32_t endian32 = 0x01234567; + const uint8_t* e8 = reinterpret_cast<const uint8_t*>(&endian32); + + CPUID::Endian_Status endian = CPUID::Endian_Status::Unknown; + + if(e8[0] == 0x01 && e8[1] == 0x23 && e8[2] == 0x45 && e8[3] == 0x67) + { + endian = CPUID::Endian_Status::Big; + } + else if(e8[0] == 0x67 && e8[1] == 0x45 && e8[2] == 0x23 && e8[3] == 0x01) + { + endian = CPUID::Endian_Status::Little; + } + else + { + throw Internal_Error("Unexpected endian at runtime, neither big nor little"); + } + + // If we were compiled with a known endian, verify it matches at runtime +#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) + BOTAN_ASSERT(endian == CPUID::Endian_Status::Little, "Build and runtime endian match"); +#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) + BOTAN_ASSERT(endian == CPUID::Endian_Status::Big, "Build and runtime endian match"); +#endif + + return endian; + } + +std::vector<Botan::CPUID::CPUID_bits> +CPUID::bit_from_string(const std::string& tok) + { +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + if(tok == "sse2" || tok == "simd") + return {Botan::CPUID::CPUID_SSE2_BIT}; + if(tok == "ssse3") + return {Botan::CPUID::CPUID_SSSE3_BIT}; + if(tok == "sse41") + return {Botan::CPUID::CPUID_SSE41_BIT}; + if(tok == "sse42") + return {Botan::CPUID::CPUID_SSE42_BIT}; + // aes_ni is the string printed on the console when running "botan cpuid" + if(tok == "aesni" || tok == "aes_ni") + return {Botan::CPUID::CPUID_AESNI_BIT}; + if(tok == "clmul") + return {Botan::CPUID::CPUID_CLMUL_BIT}; + if(tok == "avx2") + return {Botan::CPUID::CPUID_AVX2_BIT}; + if(tok == "avx512f") + return {Botan::CPUID::CPUID_AVX512F_BIT}; + if(tok == "avx512_icelake") + return {Botan::CPUID::CPUID_AVX512_ICL_BIT}; + // there were two if statements testing "sha" and "intel_sha" separately; combined + if(tok == "sha" || tok=="intel_sha") + return {Botan::CPUID::CPUID_SHA_BIT}; + if(tok == "rdtsc") + return {Botan::CPUID::CPUID_RDTSC_BIT}; + if(tok == "bmi1") + return {Botan::CPUID::CPUID_BMI1_BIT}; + if(tok == "bmi2") + return {Botan::CPUID::CPUID_BMI2_BIT}; + if(tok == "adx") + return {Botan::CPUID::CPUID_ADX_BIT}; + if(tok == "rdrand") + return {Botan::CPUID::CPUID_RDRAND_BIT}; + if(tok == "rdseed") + return {Botan::CPUID::CPUID_RDSEED_BIT}; + if(tok == "avx512_aes") + return {Botan::CPUID::CPUID_AVX512_AES_BIT}; + if(tok == "avx512_clmul") + return {Botan::CPUID::CPUID_AVX512_CLMUL_BIT}; + +#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + if(tok == "altivec" || tok == "simd") + return {Botan::CPUID::CPUID_ALTIVEC_BIT}; + if(tok == "power_crypto") + return {Botan::CPUID::CPUID_POWER_CRYPTO_BIT}; + if(tok == "darn_rng") + return {Botan::CPUID::CPUID_DARN_BIT}; + +#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) + if(tok == "neon" || tok == "simd") + return {Botan::CPUID::CPUID_ARM_NEON_BIT}; + if(tok == "arm_sve") + return {Botan::CPUID::CPUID_ARM_SVE_BIT}; + if(tok == "armv8sha1" || tok == "arm_sha1") + return {Botan::CPUID::CPUID_ARM_SHA1_BIT}; + if(tok == "armv8sha2" || tok == "arm_sha2") + return {Botan::CPUID::CPUID_ARM_SHA2_BIT}; + if(tok == "armv8aes" || tok == "arm_aes") + return {Botan::CPUID::CPUID_ARM_AES_BIT}; + if(tok == "armv8pmull" || tok == "arm_pmull") + return {Botan::CPUID::CPUID_ARM_PMULL_BIT}; + if(tok == "armv8sha3" || tok == "arm_sha3") + return {Botan::CPUID::CPUID_ARM_SHA3_BIT}; + if(tok == "armv8sha2_512" || tok == "arm_sha2_512") + return {Botan::CPUID::CPUID_ARM_SHA2_512_BIT}; + if(tok == "armv8sm3" || tok == "arm_sm3") + return {Botan::CPUID::CPUID_ARM_SM3_BIT}; + if(tok == "armv8sm4" || tok == "arm_sm4") + return {Botan::CPUID::CPUID_ARM_SM4_BIT}; + +#else + BOTAN_UNUSED(tok); +#endif + + return {}; + } + +} diff --git a/comm/third_party/botan/src/lib/utils/cpuid/cpuid.h b/comm/third_party/botan/src/lib/utils/cpuid/cpuid.h new file mode 100644 index 0000000000..04d0bbd191 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/cpuid/cpuid.h @@ -0,0 +1,484 @@ +/* +* Runtime CPU detection +* (C) 2009,2010,2013,2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CPUID_H_ +#define BOTAN_CPUID_H_ + +#include <botan/types.h> +#include <vector> +#include <string> +#include <iosfwd> + +BOTAN_FUTURE_INTERNAL_HEADER(cpuid.h) + +namespace Botan { + +/** +* A class handling runtime CPU feature detection. It is limited to +* just the features necessary to implement CPU specific code in Botan, +* rather than being a general purpose utility. +* +* This class supports: +* +* - x86 features using CPUID. x86 is also the only processor with +* accurate cache line detection currently. +* +* - PowerPC AltiVec detection on Linux, NetBSD, OpenBSD, and macOS +* +* - ARM NEON and crypto extensions detection. On Linux and Android +* systems which support getauxval, that is used to access CPU +* feature information. Otherwise a relatively portable but +* thread-unsafe mechanism involving executing probe functions which +* catching SIGILL signal is used. +*/ +class BOTAN_PUBLIC_API(2,1) CPUID final + { + public: + /** + * Probe the CPU and see what extensions are supported + */ + static void initialize(); + + static bool has_simd_32(); + + /** + * Deprecated equivalent to + * o << "CPUID flags: " << CPUID::to_string() << "\n"; + */ + BOTAN_DEPRECATED("Use CPUID::to_string") + static void print(std::ostream& o); + + /** + * Return a possibly empty string containing list of known CPU + * extensions. Each name will be seperated by a space, and the ordering + * will be arbitrary. This list only contains values that are useful to + * Botan (for example FMA instructions are not checked). + * + * Example outputs "sse2 ssse3 rdtsc", "neon arm_aes", "altivec" + */ + static std::string to_string(); + + /** + * Return a best guess of the cache line size + */ + static size_t cache_line_size() + { + return state().cache_line_size(); + } + + static bool is_little_endian() + { +#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) + return true; +#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) + return false; +#else + return state().endian_status() == Endian_Status::Little; +#endif + } + + static bool is_big_endian() + { +#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) + return true; +#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) + return false; +#else + return state().endian_status() == Endian_Status::Big; +#endif + } + + enum CPUID_bits : uint64_t { +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + // These values have no relation to cpuid bitfields + + // SIMD instruction sets + CPUID_SSE2_BIT = (1ULL << 0), + CPUID_SSSE3_BIT = (1ULL << 1), + CPUID_SSE41_BIT = (1ULL << 2), + CPUID_SSE42_BIT = (1ULL << 3), + CPUID_AVX2_BIT = (1ULL << 4), + CPUID_AVX512F_BIT = (1ULL << 5), + + CPUID_AVX512DQ_BIT = (1ULL << 6), + CPUID_AVX512BW_BIT = (1ULL << 7), + + // Ice Lake profile: AVX-512 F, DQ, BW, IFMA, VBMI, VBMI2, BITALG + CPUID_AVX512_ICL_BIT = (1ULL << 11), + + // Crypto-specific ISAs + CPUID_AESNI_BIT = (1ULL << 16), + CPUID_CLMUL_BIT = (1ULL << 17), + CPUID_RDRAND_BIT = (1ULL << 18), + CPUID_RDSEED_BIT = (1ULL << 19), + CPUID_SHA_BIT = (1ULL << 20), + CPUID_AVX512_AES_BIT = (1ULL << 21), + CPUID_AVX512_CLMUL_BIT = (1ULL << 22), + + // Misc useful instructions + CPUID_RDTSC_BIT = (1ULL << 48), + CPUID_ADX_BIT = (1ULL << 49), + CPUID_BMI1_BIT = (1ULL << 50), + CPUID_BMI2_BIT = (1ULL << 51), +#endif + +#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + CPUID_ALTIVEC_BIT = (1ULL << 0), + CPUID_POWER_CRYPTO_BIT = (1ULL << 1), + CPUID_DARN_BIT = (1ULL << 2), +#endif + +#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) + CPUID_ARM_NEON_BIT = (1ULL << 0), + CPUID_ARM_SVE_BIT = (1ULL << 1), + CPUID_ARM_AES_BIT = (1ULL << 16), + CPUID_ARM_PMULL_BIT = (1ULL << 17), + CPUID_ARM_SHA1_BIT = (1ULL << 18), + CPUID_ARM_SHA2_BIT = (1ULL << 19), + CPUID_ARM_SHA3_BIT = (1ULL << 20), + CPUID_ARM_SHA2_512_BIT = (1ULL << 21), + CPUID_ARM_SM3_BIT = (1ULL << 22), + CPUID_ARM_SM4_BIT = (1ULL << 23), +#endif + + CPUID_INITIALIZED_BIT = (1ULL << 63) + }; + +#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + /** + * Check if the processor supports AltiVec/VMX + */ + static bool has_altivec() + { return has_cpuid_bit(CPUID_ALTIVEC_BIT); } + + /** + * Check if the processor supports POWER8 crypto extensions + */ + static bool has_power_crypto() + { return has_cpuid_bit(CPUID_POWER_CRYPTO_BIT); } + + /** + * Check if the processor supports POWER9 DARN RNG + */ + static bool has_darn_rng() + { return has_cpuid_bit(CPUID_DARN_BIT); } + +#endif + +#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) + /** + * Check if the processor supports NEON SIMD + */ + static bool has_neon() + { return has_cpuid_bit(CPUID_ARM_NEON_BIT); } + + /** + * Check if the processor supports ARMv8 SVE + */ + static bool has_arm_sve() + { return has_cpuid_bit(CPUID_ARM_SVE_BIT); } + + /** + * Check if the processor supports ARMv8 SHA1 + */ + static bool has_arm_sha1() + { return has_cpuid_bit(CPUID_ARM_SHA1_BIT); } + + /** + * Check if the processor supports ARMv8 SHA2 + */ + static bool has_arm_sha2() + { return has_cpuid_bit(CPUID_ARM_SHA2_BIT); } + + /** + * Check if the processor supports ARMv8 AES + */ + static bool has_arm_aes() + { return has_cpuid_bit(CPUID_ARM_AES_BIT); } + + /** + * Check if the processor supports ARMv8 PMULL + */ + static bool has_arm_pmull() + { return has_cpuid_bit(CPUID_ARM_PMULL_BIT); } + + /** + * Check if the processor supports ARMv8 SHA-512 + */ + static bool has_arm_sha2_512() + { return has_cpuid_bit(CPUID_ARM_SHA2_512_BIT); } + + /** + * Check if the processor supports ARMv8 SHA-3 + */ + static bool has_arm_sha3() + { return has_cpuid_bit(CPUID_ARM_SHA3_BIT); } + + /** + * Check if the processor supports ARMv8 SM3 + */ + static bool has_arm_sm3() + { return has_cpuid_bit(CPUID_ARM_SM3_BIT); } + + /** + * Check if the processor supports ARMv8 SM4 + */ + static bool has_arm_sm4() + { return has_cpuid_bit(CPUID_ARM_SM4_BIT); } + +#endif + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + + /** + * Check if the processor supports RDTSC + */ + static bool has_rdtsc() + { return has_cpuid_bit(CPUID_RDTSC_BIT); } + + /** + * Check if the processor supports SSE2 + */ + static bool has_sse2() + { return has_cpuid_bit(CPUID_SSE2_BIT); } + + /** + * Check if the processor supports SSSE3 + */ + static bool has_ssse3() + { return has_cpuid_bit(CPUID_SSSE3_BIT); } + + /** + * Check if the processor supports SSE4.1 + */ + static bool has_sse41() + { return has_cpuid_bit(CPUID_SSE41_BIT); } + + /** + * Check if the processor supports SSE4.2 + */ + static bool has_sse42() + { return has_cpuid_bit(CPUID_SSE42_BIT); } + + /** + * Check if the processor supports AVX2 + */ + static bool has_avx2() + { return has_cpuid_bit(CPUID_AVX2_BIT); } + + /** + * Check if the processor supports AVX-512F + */ + static bool has_avx512f() + { return has_cpuid_bit(CPUID_AVX512F_BIT); } + + /** + * Check if the processor supports AVX-512DQ + */ + static bool has_avx512dq() + { return has_cpuid_bit(CPUID_AVX512DQ_BIT); } + + /** + * Check if the processor supports AVX-512BW + */ + static bool has_avx512bw() + { return has_cpuid_bit(CPUID_AVX512BW_BIT); } + + /** + * Check if the processor supports AVX-512 Ice Lake profile + */ + static bool has_avx512_icelake() + { return has_cpuid_bit(CPUID_AVX512_ICL_BIT); } + + /** + * Check if the processor supports AVX-512 AES (VAES) + */ + static bool has_avx512_aes() + { return has_cpuid_bit(CPUID_AVX512_AES_BIT); } + + /** + * Check if the processor supports AVX-512 VPCLMULQDQ + */ + static bool has_avx512_clmul() + { return has_cpuid_bit(CPUID_AVX512_CLMUL_BIT); } + + /** + * Check if the processor supports BMI1 + */ + static bool has_bmi1() + { return has_cpuid_bit(CPUID_BMI1_BIT); } + + /** + * Check if the processor supports BMI2 + */ + static bool has_bmi2() + { return has_cpuid_bit(CPUID_BMI2_BIT); } + + /** + * Check if the processor supports AES-NI + */ + static bool has_aes_ni() + { return has_cpuid_bit(CPUID_AESNI_BIT); } + + /** + * Check if the processor supports CLMUL + */ + static bool has_clmul() + { return has_cpuid_bit(CPUID_CLMUL_BIT); } + + /** + * Check if the processor supports Intel SHA extension + */ + static bool has_intel_sha() + { return has_cpuid_bit(CPUID_SHA_BIT); } + + /** + * Check if the processor supports ADX extension + */ + static bool has_adx() + { return has_cpuid_bit(CPUID_ADX_BIT); } + + /** + * Check if the processor supports RDRAND + */ + static bool has_rdrand() + { return has_cpuid_bit(CPUID_RDRAND_BIT); } + + /** + * Check if the processor supports RDSEED + */ + static bool has_rdseed() + { return has_cpuid_bit(CPUID_RDSEED_BIT); } +#endif + + /** + * Check if the processor supports byte-level vector permutes + * (SSSE3, NEON, Altivec) + */ + static bool has_vperm() + { +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + return has_ssse3(); +#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) + return has_neon(); +#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + return has_altivec(); +#else + return false; +#endif + } + + /** + * Check if the processor supports hardware AES instructions + */ + static bool has_hw_aes() + { +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + return has_aes_ni(); +#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) + return has_arm_aes(); +#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + return has_power_crypto(); +#else + return false; +#endif + } + + /** + * Check if the processor supports carryless multiply + * (CLMUL, PMULL) + */ + static bool has_carryless_multiply() + { +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + return has_clmul(); +#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) + return has_arm_pmull(); +#elif defined(BOTAN_TARGET_ARCH_IS_PPC64) + return has_power_crypto(); +#else + return false; +#endif + } + + /* + * Clear a CPUID bit + * Call CPUID::initialize to reset + * + * This is only exposed for testing, don't use unless you know + * what you are doing. + */ + static void clear_cpuid_bit(CPUID_bits bit) + { + state().clear_cpuid_bit(static_cast<uint64_t>(bit)); + } + + /* + * Don't call this function, use CPUID::has_xxx above + * It is only exposed for the tests. + */ + static bool has_cpuid_bit(CPUID_bits elem) + { + const uint64_t elem64 = static_cast<uint64_t>(elem); + return state().has_bit(elem64); + } + + static std::vector<CPUID::CPUID_bits> bit_from_string(const std::string& tok); + private: + enum class Endian_Status : uint32_t { + Unknown = 0x00000000, + Big = 0x01234567, + Little = 0x67452301, + }; + + struct CPUID_Data + { + public: + CPUID_Data(); + + CPUID_Data(const CPUID_Data& other) = default; + CPUID_Data& operator=(const CPUID_Data& other) = default; + + void clear_cpuid_bit(uint64_t bit) + { + m_processor_features &= ~bit; + } + + bool has_bit(uint64_t bit) const + { + return (m_processor_features & bit) == bit; + } + + uint64_t processor_features() const { return m_processor_features; } + Endian_Status endian_status() const { return m_endian_status; } + size_t cache_line_size() const { return m_cache_line_size; } + + private: + static Endian_Status runtime_check_endian(); + +#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \ + defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \ + defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + + static uint64_t detect_cpu_features(size_t* cache_line_size); + +#endif + uint64_t m_processor_features; + size_t m_cache_line_size; + Endian_Status m_endian_status; + }; + + static CPUID_Data& state() + { + static CPUID::CPUID_Data g_cpuid; + return g_cpuid; + } + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/cpuid/cpuid_arm.cpp b/comm/third_party/botan/src/lib/utils/cpuid/cpuid_arm.cpp new file mode 100644 index 0000000000..0711dfdf39 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/cpuid/cpuid_arm.cpp @@ -0,0 +1,237 @@ +/* +* Runtime CPU detection for ARM +* (C) 2009,2010,2013,2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/cpuid.h> + +#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) + +#if defined(BOTAN_TARGET_OS_IS_IOS) + #include <sys/types.h> + #include <sys/sysctl.h> + +#else + #include <botan/internal/os_utils.h> + +#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO) + #include <sys/auxv.h> +#endif + +#endif + +#endif + +namespace Botan { + +#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) + +#if defined(BOTAN_TARGET_OS_IS_IOS) + +namespace { + +uint64_t flags_by_ios_machine_type(const std::string& machine) + { + /* + * This relies on a map of known machine names to features. This + * will quickly grow out of date as new products are introduced, but + * is apparently the best we can do for iOS. + */ + + struct version_info { + std::string name; + size_t min_version_neon; + size_t min_version_armv8; + }; + + static const version_info min_versions[] = { + { "iPhone", 2, 6 }, + { "iPad", 1, 4 }, + { "iPod", 4, 7 }, + { "AppleTV", 2, 5 }, + }; + + if(machine.size() < 3) + return 0; + + auto comma = machine.find(','); + + // Simulator, or something we don't know about + if(comma == std::string::npos) + return 0; + + std::string product = machine.substr(0, comma); + + size_t version = 0; + size_t place = 1; + while(product.size() > 1 && ::isdigit(product.back())) + { + const size_t digit = product.back() - '0'; + version += digit * place; + place *= 10; + product.pop_back(); + } + + if(version == 0) + return 0; + + for(const version_info& info : min_versions) + { + if(info.name != product) + continue; + + if(version >= info.min_version_armv8) + { + return CPUID::CPUID_ARM_AES_BIT | + CPUID::CPUID_ARM_PMULL_BIT | + CPUID::CPUID_ARM_SHA1_BIT | + CPUID::CPUID_ARM_SHA2_BIT | + CPUID::CPUID_ARM_NEON_BIT; + } + + if(version >= info.min_version_neon) + return CPUID::CPUID_ARM_NEON_BIT; + } + + // Some other product we don't know about + return 0; + } + +} + +#endif + +uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size) + { + BOTAN_UNUSED(cache_line_size); + + uint64_t detected_features = 0; + +#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO) + /* + * On systems with getauxval these bits should normally be defined + * in bits/auxv.h but some buggy? glibc installs seem to miss them. + * These following values are all fixed, for the Linux ELF format, + * so we just hardcode them in ARM_hwcap_bit enum. + */ + + enum ARM_hwcap_bit { +#if defined(BOTAN_TARGET_ARCH_IS_ARM32) + NEON_bit = (1 << 12), + AES_bit = (1 << 0), + PMULL_bit = (1 << 1), + SHA1_bit = (1 << 2), + SHA2_bit = (1 << 3), + + ARCH_hwcap_neon = 16, // AT_HWCAP + ARCH_hwcap_crypto = 26, // AT_HWCAP2 +#elif defined(BOTAN_TARGET_ARCH_IS_ARM64) + NEON_bit = (1 << 1), + AES_bit = (1 << 3), + PMULL_bit = (1 << 4), + SHA1_bit = (1 << 5), + SHA2_bit = (1 << 6), + SHA3_bit = (1 << 17), + SM3_bit = (1 << 18), + SM4_bit = (1 << 19), + SHA2_512_bit = (1 << 21), + SVE_bit = (1 << 22), + + ARCH_hwcap_neon = 16, // AT_HWCAP + ARCH_hwcap_crypto = 16, // AT_HWCAP +#endif + }; + +#if defined(AT_DCACHEBSIZE) + // Exists only on Linux + const unsigned long dcache_line = OS::get_auxval(AT_DCACHEBSIZE); + + // plausibility check + if(dcache_line == 32 || dcache_line == 64 || dcache_line == 128) + *cache_line_size = static_cast<size_t>(dcache_line); +#endif + + const unsigned long hwcap_neon = OS::get_auxval(ARM_hwcap_bit::ARCH_hwcap_neon); + if(hwcap_neon & ARM_hwcap_bit::NEON_bit) + detected_features |= CPUID::CPUID_ARM_NEON_BIT; + + /* + On aarch64 this ends up calling getauxval twice with AT_HWCAP + It doesn't seem worth optimizing this out, since getauxval is + just reading a field in the ELF header. + */ + const unsigned long hwcap_crypto = OS::get_auxval(ARM_hwcap_bit::ARCH_hwcap_crypto); + if(hwcap_crypto & ARM_hwcap_bit::AES_bit) + detected_features |= CPUID::CPUID_ARM_AES_BIT; + if(hwcap_crypto & ARM_hwcap_bit::PMULL_bit) + detected_features |= CPUID::CPUID_ARM_PMULL_BIT; + if(hwcap_crypto & ARM_hwcap_bit::SHA1_bit) + detected_features |= CPUID::CPUID_ARM_SHA1_BIT; + if(hwcap_crypto & ARM_hwcap_bit::SHA2_bit) + detected_features |= CPUID::CPUID_ARM_SHA2_BIT; + +#if defined(BOTAN_TARGET_ARCH_IS_ARM64) + if(hwcap_crypto & ARM_hwcap_bit::SHA3_bit) + detected_features |= CPUID::CPUID_ARM_SHA3_BIT; + if(hwcap_crypto & ARM_hwcap_bit::SM3_bit) + detected_features |= CPUID::CPUID_ARM_SM3_BIT; + if(hwcap_crypto & ARM_hwcap_bit::SM4_bit) + detected_features |= CPUID::CPUID_ARM_SM4_BIT; + if(hwcap_crypto & ARM_hwcap_bit::SHA2_512_bit) + detected_features |= CPUID::CPUID_ARM_SHA2_512_BIT; + if(hwcap_crypto & ARM_hwcap_bit::SVE_bit) + detected_features |= CPUID::CPUID_ARM_SVE_BIT; +#endif + +#elif defined(BOTAN_TARGET_OS_IS_IOS) + + char machine[64] = { 0 }; + size_t size = sizeof(machine) - 1; + ::sysctlbyname("hw.machine", machine, &size, nullptr, 0); + + detected_features = flags_by_ios_machine_type(machine); + // No way to detect cache line size on iOS? + +#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_ARM64) + + /* + No getauxval API available, fall back on probe functions. We only + bother with Aarch64 here to simplify the code and because going to + extreme contortions to support detect NEON on devices that probably + don't support it doesn't seem worthwhile. + + NEON registers v0-v7 are caller saved in Aarch64 + */ + + auto neon_probe = []() noexcept -> int { asm("and v0.16b, v0.16b, v0.16b"); return 1; }; + auto aes_probe = []() noexcept -> int { asm(".word 0x4e284800"); return 1; }; + auto pmull_probe = []() noexcept -> int { asm(".word 0x0ee0e000"); return 1; }; + auto sha1_probe = []() noexcept -> int { asm(".word 0x5e280800"); return 1; }; + auto sha2_probe = []() noexcept -> int { asm(".word 0x5e282800"); return 1; }; + + // Only bother running the crypto detection if we found NEON + + if(OS::run_cpu_instruction_probe(neon_probe) == 1) + { + detected_features |= CPUID::CPUID_ARM_NEON_BIT; + + if(OS::run_cpu_instruction_probe(aes_probe) == 1) + detected_features |= CPUID::CPUID_ARM_AES_BIT; + if(OS::run_cpu_instruction_probe(pmull_probe) == 1) + detected_features |= CPUID::CPUID_ARM_PMULL_BIT; + if(OS::run_cpu_instruction_probe(sha1_probe) == 1) + detected_features |= CPUID::CPUID_ARM_SHA1_BIT; + if(OS::run_cpu_instruction_probe(sha2_probe) == 1) + detected_features |= CPUID::CPUID_ARM_SHA2_BIT; + } + +#endif + + return detected_features; + } + +#endif + +} diff --git a/comm/third_party/botan/src/lib/utils/cpuid/cpuid_ppc.cpp b/comm/third_party/botan/src/lib/utils/cpuid/cpuid_ppc.cpp new file mode 100644 index 0000000000..604b97947c --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/cpuid/cpuid_ppc.cpp @@ -0,0 +1,132 @@ +/* +* Runtime CPU detection for POWER/PowerPC +* (C) 2009,2010,2013,2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/cpuid.h> +#include <botan/internal/os_utils.h> + +#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + +/* +* On macOS and OpenBSD ppc, use sysctl to detect AltiVec +*/ +#if defined(BOTAN_TARGET_OS_IS_MACOS) + #include <sys/sysctl.h> +#elif defined(BOTAN_TARGET_OS_IS_OPENBSD) + #include <sys/param.h> + #include <sys/sysctl.h> + #include <machine/cpu.h> +#endif + +#endif + +namespace Botan { + +#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + +/* +* PowerPC specific block: check for AltiVec using either +* sysctl or by reading processor version number register. +*/ +uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size) + { + BOTAN_UNUSED(cache_line_size); + +#if defined(BOTAN_TARGET_OS_IS_MACOS) || defined(BOTAN_TARGET_OS_IS_OPENBSD) + // On macOS and OpenBSD, use sysctl + + int sels[2] = { +#if defined(BOTAN_TARGET_OS_IS_OPENBSD) + CTL_MACHDEP, CPU_ALTIVEC +#else + CTL_HW, HW_VECTORUNIT +#endif + }; + + int vector_type = 0; + size_t length = sizeof(vector_type); + int error = ::sysctl(sels, 2, &vector_type, &length, NULL, 0); + + if(error == 0 && vector_type > 0) + return CPUID::CPUID_ALTIVEC_BIT; + +#elif (defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_HAS_ELF_AUX_INFO)) && defined(BOTAN_TARGET_ARCH_IS_PPC64) + + enum PPC_hwcap_bit { + ALTIVEC_bit = (1 << 28), + CRYPTO_bit = (1 << 25), + DARN_bit = (1 << 21), + + ARCH_hwcap_altivec = 16, // AT_HWCAP + ARCH_hwcap_crypto = 26, // AT_HWCAP2 + }; + + uint64_t detected_features = 0; + + const unsigned long hwcap_altivec = OS::get_auxval(PPC_hwcap_bit::ARCH_hwcap_altivec); + if(hwcap_altivec & PPC_hwcap_bit::ALTIVEC_bit) + detected_features |= CPUID::CPUID_ALTIVEC_BIT; + + const unsigned long hwcap_crypto = OS::get_auxval(PPC_hwcap_bit::ARCH_hwcap_crypto); + if(hwcap_crypto & PPC_hwcap_bit::CRYPTO_bit) + detected_features |= CPUID::CPUID_POWER_CRYPTO_BIT; + if(hwcap_crypto & PPC_hwcap_bit::DARN_bit) + detected_features |= CPUID::CPUID_DARN_BIT; + + return detected_features; + +#else + + /* + On PowerPC, MSR 287 is PVR, the Processor Version Number + Normally it is only accessible to ring 0, but Linux and NetBSD + (others, too, maybe?) will trap and emulate it for us. + */ + + int pvr = OS::run_cpu_instruction_probe([]() noexcept -> int { + uint32_t pvr = 0; + asm volatile("mfspr %0, 287" : "=r" (pvr)); + // Top 16 bits suffice to identify the model + return static_cast<int>(pvr >> 16); + }); + + if(pvr > 0) + { + const uint16_t ALTIVEC_PVR[] = { + 0x003E, // IBM POWER6 + 0x003F, // IBM POWER7 + 0x004A, // IBM POWER7p + 0x004B, // IBM POWER8E + 0x004C, // IBM POWER8 NVL + 0x004D, // IBM POWER8 + 0x004E, // IBM POWER9 + 0x000C, // G4-7400 + 0x0039, // G5 970 + 0x003C, // G5 970FX + 0x0044, // G5 970MP + 0x0070, // Cell PPU + 0, // end + }; + + for(size_t i = 0; ALTIVEC_PVR[i]; ++i) + { + if(pvr == ALTIVEC_PVR[i]) + return CPUID::CPUID_ALTIVEC_BIT; + } + + return 0; + } + + // TODO try direct instruction probing + +#endif + + return 0; + } + +#endif + +} diff --git a/comm/third_party/botan/src/lib/utils/cpuid/cpuid_x86.cpp b/comm/third_party/botan/src/lib/utils/cpuid/cpuid_x86.cpp new file mode 100644 index 0000000000..0595e1a604 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/cpuid/cpuid_x86.cpp @@ -0,0 +1,214 @@ +/* +* Runtime CPU detection for x86 +* (C) 2009,2010,2013,2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/cpuid.h> +#include <botan/mem_ops.h> +#include <botan/loadstor.h> + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + +#if defined(BOTAN_BUILD_COMPILER_IS_MSVC) + #include <intrin.h> +#elif defined(BOTAN_BUILD_COMPILER_IS_INTEL) + #include <ia32intrin.h> +#elif defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG) + #include <cpuid.h> +#endif + +#endif + +namespace Botan { + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + +uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size) + { +#if defined(BOTAN_BUILD_COMPILER_IS_MSVC) + #define X86_CPUID(type, out) do { __cpuid((int*)out, type); } while(0) + #define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0) + +#elif defined(BOTAN_BUILD_COMPILER_IS_INTEL) + #define X86_CPUID(type, out) do { __cpuid(out, type); } while(0) + #define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0) + +#elif defined(BOTAN_TARGET_ARCH_IS_X86_64) && defined(BOTAN_USE_GCC_INLINE_ASM) + #define X86_CPUID(type, out) \ + asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \ + : "0" (type)) + + #define X86_CPUID_SUBLEVEL(type, level, out) \ + asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \ + : "0" (type), "2" (level)) + +#elif defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG) + #define X86_CPUID(type, out) do { __get_cpuid(type, out, out+1, out+2, out+3); } while(0) + + #define X86_CPUID_SUBLEVEL(type, level, out) \ + do { __cpuid_count(type, level, out[0], out[1], out[2], out[3]); } while(0) +#else + #warning "No way of calling x86 cpuid instruction for this compiler" + #define X86_CPUID(type, out) do { clear_mem(out, 4); } while(0) + #define X86_CPUID_SUBLEVEL(type, level, out) do { clear_mem(out, 4); } while(0) +#endif + + uint64_t features_detected = 0; + uint32_t cpuid[4] = { 0 }; + + // CPUID 0: vendor identification, max sublevel + X86_CPUID(0, cpuid); + + const uint32_t max_supported_sublevel = cpuid[0]; + + const uint32_t INTEL_CPUID[3] = { 0x756E6547, 0x6C65746E, 0x49656E69 }; + const uint32_t AMD_CPUID[3] = { 0x68747541, 0x444D4163, 0x69746E65 }; + const bool is_intel = same_mem(cpuid + 1, INTEL_CPUID, 3); + const bool is_amd = same_mem(cpuid + 1, AMD_CPUID, 3); + + if(max_supported_sublevel >= 1) + { + // CPUID 1: feature bits + X86_CPUID(1, cpuid); + const uint64_t flags0 = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[3]; + + enum x86_CPUID_1_bits : uint64_t { + RDTSC = (1ULL << 4), + SSE2 = (1ULL << 26), + CLMUL = (1ULL << 33), + SSSE3 = (1ULL << 41), + SSE41 = (1ULL << 51), + SSE42 = (1ULL << 52), + AESNI = (1ULL << 57), + RDRAND = (1ULL << 62) + }; + + if(flags0 & x86_CPUID_1_bits::RDTSC) + features_detected |= CPUID::CPUID_RDTSC_BIT; + if(flags0 & x86_CPUID_1_bits::SSE2) + features_detected |= CPUID::CPUID_SSE2_BIT; + if(flags0 & x86_CPUID_1_bits::CLMUL) + features_detected |= CPUID::CPUID_CLMUL_BIT; + if(flags0 & x86_CPUID_1_bits::SSSE3) + features_detected |= CPUID::CPUID_SSSE3_BIT; + if(flags0 & x86_CPUID_1_bits::SSE41) + features_detected |= CPUID::CPUID_SSE41_BIT; + if(flags0 & x86_CPUID_1_bits::SSE42) + features_detected |= CPUID::CPUID_SSE42_BIT; + if(flags0 & x86_CPUID_1_bits::AESNI) + features_detected |= CPUID::CPUID_AESNI_BIT; + if(flags0 & x86_CPUID_1_bits::RDRAND) + features_detected |= CPUID::CPUID_RDRAND_BIT; + } + + if(is_intel) + { + // Intel cache line size is in cpuid(1) output + *cache_line_size = 8 * get_byte(2, cpuid[1]); + } + else if(is_amd) + { + // AMD puts it in vendor zone + X86_CPUID(0x80000005, cpuid); + *cache_line_size = get_byte(3, cpuid[2]); + } + + if(max_supported_sublevel >= 7) + { + clear_mem(cpuid, 4); + X86_CPUID_SUBLEVEL(7, 0, cpuid); + + enum x86_CPUID_7_bits : uint64_t { + BMI1 = (1ULL << 3), + AVX2 = (1ULL << 5), + BMI2 = (1ULL << 8), + AVX512_F = (1ULL << 16), + AVX512_DQ = (1ULL << 17), + RDSEED = (1ULL << 18), + ADX = (1ULL << 19), + AVX512_IFMA = (1ULL << 21), + SHA = (1ULL << 29), + AVX512_BW = (1ULL << 30), + AVX512_VL = (1ULL << 31), + AVX512_VBMI = (1ULL << 33), + AVX512_VBMI2 = (1ULL << 38), + AVX512_VAES = (1ULL << 41), + AVX512_VCLMUL = (1ULL << 42), + AVX512_VBITALG = (1ULL << 44), + }; + + const uint64_t flags7 = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[1]; + + if(flags7 & x86_CPUID_7_bits::AVX2) + features_detected |= CPUID::CPUID_AVX2_BIT; + if(flags7 & x86_CPUID_7_bits::BMI1) + { + features_detected |= CPUID::CPUID_BMI1_BIT; + /* + We only set the BMI2 bit if BMI1 is also supported, so BMI2 + code can safely use both extensions. No known processor + implements BMI2 but not BMI1. + */ + if(flags7 & x86_CPUID_7_bits::BMI2) + features_detected |= CPUID::CPUID_BMI2_BIT; + } + + if(flags7 & x86_CPUID_7_bits::AVX512_F) + { + features_detected |= CPUID::CPUID_AVX512F_BIT; + + if(flags7 & x86_CPUID_7_bits::AVX512_DQ) + features_detected |= CPUID::CPUID_AVX512DQ_BIT; + if(flags7 & x86_CPUID_7_bits::AVX512_BW) + features_detected |= CPUID::CPUID_AVX512BW_BIT; + + const uint64_t ICELAKE_FLAGS = + x86_CPUID_7_bits::AVX512_F | + x86_CPUID_7_bits::AVX512_DQ | + x86_CPUID_7_bits::AVX512_IFMA | + x86_CPUID_7_bits::AVX512_BW | + x86_CPUID_7_bits::AVX512_VL | + x86_CPUID_7_bits::AVX512_VBMI | + x86_CPUID_7_bits::AVX512_VBMI2 | + x86_CPUID_7_bits::AVX512_VBITALG; + + if((flags7 & ICELAKE_FLAGS) == ICELAKE_FLAGS) + features_detected |= CPUID::CPUID_AVX512_ICL_BIT; + + if(flags7 & x86_CPUID_7_bits::AVX512_VAES) + features_detected |= CPUID::CPUID_AVX512_AES_BIT; + if(flags7 & x86_CPUID_7_bits::AVX512_VCLMUL) + features_detected |= CPUID::CPUID_AVX512_CLMUL_BIT; + } + + if(flags7 & x86_CPUID_7_bits::RDSEED) + features_detected |= CPUID::CPUID_RDSEED_BIT; + if(flags7 & x86_CPUID_7_bits::ADX) + features_detected |= CPUID::CPUID_ADX_BIT; + if(flags7 & x86_CPUID_7_bits::SHA) + features_detected |= CPUID::CPUID_SHA_BIT; + } + +#undef X86_CPUID +#undef X86_CPUID_SUBLEVEL + + /* + * If we don't have access to CPUID, we can still safely assume that + * any x86-64 processor has SSE2 and RDTSC + */ +#if defined(BOTAN_TARGET_ARCH_IS_X86_64) + if(features_detected == 0) + { + features_detected |= CPUID::CPUID_SSE2_BIT; + features_detected |= CPUID::CPUID_RDTSC_BIT; + } +#endif + + return features_detected; + } + +#endif + +} diff --git a/comm/third_party/botan/src/lib/utils/cpuid/info.txt b/comm/third_party/botan/src/lib/utils/cpuid/info.txt new file mode 100644 index 0000000000..987d7eae4d --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/cpuid/info.txt @@ -0,0 +1,7 @@ +<defines> +CPUID -> 20170917 +</defines> + +<header:public> +cpuid.h +</header:public> diff --git a/comm/third_party/botan/src/lib/utils/ct_utils.cpp b/comm/third_party/botan/src/lib/utils/ct_utils.cpp new file mode 100644 index 0000000000..cbcecfc282 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/ct_utils.cpp @@ -0,0 +1,83 @@ +/* +* (C) 2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/ct_utils.h> + +namespace Botan { + +namespace CT { + +secure_vector<uint8_t> copy_output(CT::Mask<uint8_t> bad_input, + const uint8_t input[], + size_t input_length, + size_t offset) + { + if(input_length == 0) + return secure_vector<uint8_t>(); + + /* + * Ensure at runtime that offset <= input_length. This is an invalid input, + * but we can't throw without using the poisoned value. Instead, if it happens, + * set offset to be equal to the input length (so output_bytes becomes 0 and + * the returned vector is empty) + */ + const auto valid_offset = CT::Mask<size_t>::is_lte(offset, input_length); + offset = valid_offset.select(offset, input_length); + + const size_t output_bytes = input_length - offset; + + secure_vector<uint8_t> output(input_length); + + /* + Move the desired output bytes to the front using a slow (O^n) + but constant time loop that does not leak the value of the offset + */ + for(size_t i = 0; i != input_length; ++i) + { + /* + start index from i rather than 0 since we know j must be >= i + offset + to have any effect, and starting from i does not reveal information + */ + for(size_t j = i; j != input_length; ++j) + { + const uint8_t b = input[j]; + const auto is_eq = CT::Mask<size_t>::is_equal(j, offset + i); + output[i] |= is_eq.if_set_return(b); + } + } + + bad_input.if_set_zero_out(output.data(), output.size()); + + CT::unpoison(output.data(), output.size()); + CT::unpoison(output_bytes); + + /* + This is potentially not const time, depending on how std::vector is + implemented. But since we are always reducing length, it should + just amount to setting the member var holding the length. + */ + output.resize(output_bytes); + return output; + } + +secure_vector<uint8_t> strip_leading_zeros(const uint8_t in[], size_t length) + { + size_t leading_zeros = 0; + + auto only_zeros = Mask<uint8_t>::set(); + + for(size_t i = 0; i != length; ++i) + { + only_zeros &= CT::Mask<uint8_t>::is_zero(in[i]); + leading_zeros += only_zeros.if_set_return(1); + } + + return copy_output(CT::Mask<uint8_t>::cleared(), in, length, leading_zeros); + } + +} + +} diff --git a/comm/third_party/botan/src/lib/utils/ct_utils.h b/comm/third_party/botan/src/lib/utils/ct_utils.h new file mode 100644 index 0000000000..f2e7452931 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/ct_utils.h @@ -0,0 +1,418 @@ +/* +* Functions for constant time operations on data and testing of +* constant time annotations using valgrind. +* +* For more information about constant time programming see +* Wagner, Molnar, et al "The Program Counter Security Model" +* +* (C) 2010 Falko Strenzke +* (C) 2015,2016,2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CT_UTILS_H_ +#define BOTAN_CT_UTILS_H_ + +#include <botan/secmem.h> +#include <botan/internal/bit_ops.h> +#include <type_traits> +#include <vector> + +#if defined(BOTAN_HAS_VALGRIND) + #include <valgrind/memcheck.h> +#endif + +namespace Botan { + +namespace CT { + +/** +* Use valgrind to mark the contents of memory as being undefined. +* Valgrind will accept operations which manipulate undefined values, +* but will warn if an undefined value is used to decided a conditional +* jump or a load/store address. So if we poison all of our inputs we +* can confirm that the operations in question are truly const time +* when compiled by whatever compiler is in use. +* +* Even better, the VALGRIND_MAKE_MEM_* macros work even when the +* program is not run under valgrind (though with a few cycles of +* overhead, which is unfortunate in final binaries as these +* annotations tend to be used in fairly important loops). +* +* This approach was first used in ctgrind (https://github.com/agl/ctgrind) +* but calling the valgrind mecheck API directly works just as well and +* doesn't require a custom patched valgrind. +*/ +template<typename T> +inline void poison(const T* p, size_t n) + { +#if defined(BOTAN_HAS_VALGRIND) + VALGRIND_MAKE_MEM_UNDEFINED(p, n * sizeof(T)); +#else + BOTAN_UNUSED(p); + BOTAN_UNUSED(n); +#endif + } + +template<typename T> +inline void unpoison(const T* p, size_t n) + { +#if defined(BOTAN_HAS_VALGRIND) + VALGRIND_MAKE_MEM_DEFINED(p, n * sizeof(T)); +#else + BOTAN_UNUSED(p); + BOTAN_UNUSED(n); +#endif + } + +template<typename T> +inline void unpoison(T& p) + { +#if defined(BOTAN_HAS_VALGRIND) + VALGRIND_MAKE_MEM_DEFINED(&p, sizeof(T)); +#else + BOTAN_UNUSED(p); +#endif + } + +/** +* A Mask type used for constant-time operations. A Mask<T> always has value +* either 0 (all bits cleared) or ~0 (all bits set). All operations in a Mask<T> +* are intended to compile to code which does not contain conditional jumps. +* This must be verified with tooling (eg binary disassembly or using valgrind) +* since you never know what a compiler might do. +*/ +template<typename T> +class Mask + { + public: + static_assert(std::is_unsigned<T>::value, "CT::Mask only defined for unsigned integer types"); + + Mask(const Mask<T>& other) = default; + Mask<T>& operator=(const Mask<T>& other) = default; + + /** + * Derive a Mask from a Mask of a larger type + */ + template<typename U> + Mask(Mask<U> o) : m_mask(static_cast<T>(o.value())) + { + static_assert(sizeof(U) > sizeof(T), "sizes ok"); + } + + /** + * Return a Mask<T> with all bits set + */ + static Mask<T> set() + { + return Mask<T>(static_cast<T>(~0)); + } + + /** + * Return a Mask<T> with all bits cleared + */ + static Mask<T> cleared() + { + return Mask<T>(0); + } + + /** + * Return a Mask<T> which is set if v is != 0 + */ + static Mask<T> expand(T v) + { + return ~Mask<T>::is_zero(v); + } + + /** + * Return a Mask<T> which is set if m is set + */ + template<typename U> + static Mask<T> expand(Mask<U> m) + { + static_assert(sizeof(U) < sizeof(T), "sizes ok"); + return ~Mask<T>::is_zero(m.value()); + } + + /** + * Return a Mask<T> which is set if v is == 0 or cleared otherwise + */ + static Mask<T> is_zero(T x) + { + return Mask<T>(ct_is_zero<T>(x)); + } + + /** + * Return a Mask<T> which is set if x == y + */ + static Mask<T> is_equal(T x, T y) + { + return Mask<T>::is_zero(static_cast<T>(x ^ y)); + } + + /** + * Return a Mask<T> which is set if x < y + */ + static Mask<T> is_lt(T x, T y) + { + return Mask<T>(expand_top_bit<T>(x^((x^y) | ((x-y)^x)))); + } + + /** + * Return a Mask<T> which is set if x > y + */ + static Mask<T> is_gt(T x, T y) + { + return Mask<T>::is_lt(y, x); + } + + /** + * Return a Mask<T> which is set if x <= y + */ + static Mask<T> is_lte(T x, T y) + { + return ~Mask<T>::is_gt(x, y); + } + + /** + * Return a Mask<T> which is set if x >= y + */ + static Mask<T> is_gte(T x, T y) + { + return ~Mask<T>::is_lt(x, y); + } + + static Mask<T> is_within_range(T v, T l, T u) + { + //return Mask<T>::is_gte(v, l) & Mask<T>::is_lte(v, u); + + const T v_lt_l = v^((v^l) | ((v-l)^v)); + const T v_gt_u = u^((u^v) | ((u-v)^u)); + const T either = v_lt_l | v_gt_u; + return ~Mask<T>(expand_top_bit(either)); + } + + static Mask<T> is_any_of(T v, std::initializer_list<T> accepted) + { + T accept = 0; + + for(auto a: accepted) + { + const T diff = a ^ v; + const T eq_zero = ~diff & (diff - 1); + accept |= eq_zero; + } + + return Mask<T>(expand_top_bit(accept)); + } + + /** + * AND-combine two masks + */ + Mask<T>& operator&=(Mask<T> o) + { + m_mask &= o.value(); + return (*this); + } + + /** + * XOR-combine two masks + */ + Mask<T>& operator^=(Mask<T> o) + { + m_mask ^= o.value(); + return (*this); + } + + /** + * OR-combine two masks + */ + Mask<T>& operator|=(Mask<T> o) + { + m_mask |= o.value(); + return (*this); + } + + /** + * AND-combine two masks + */ + friend Mask<T> operator&(Mask<T> x, Mask<T> y) + { + return Mask<T>(x.value() & y.value()); + } + + /** + * XOR-combine two masks + */ + friend Mask<T> operator^(Mask<T> x, Mask<T> y) + { + return Mask<T>(x.value() ^ y.value()); + } + + /** + * OR-combine two masks + */ + friend Mask<T> operator|(Mask<T> x, Mask<T> y) + { + return Mask<T>(x.value() | y.value()); + } + + /** + * Negate this mask + */ + Mask<T> operator~() const + { + return Mask<T>(~value()); + } + + /** + * Return x if the mask is set, or otherwise zero + */ + T if_set_return(T x) const + { + return m_mask & x; + } + + /** + * Return x if the mask is cleared, or otherwise zero + */ + T if_not_set_return(T x) const + { + return ~m_mask & x; + } + + /** + * If this mask is set, return x, otherwise return y + */ + T select(T x, T y) const + { + // (x & value()) | (y & ~value()) + return static_cast<T>(y ^ (value() & (x ^ y))); + } + + T select_and_unpoison(T x, T y) const + { + T r = this->select(x, y); + CT::unpoison(r); + return r; + } + + /** + * If this mask is set, return x, otherwise return y + */ + Mask<T> select_mask(Mask<T> x, Mask<T> y) const + { + return Mask<T>(select(x.value(), y.value())); + } + + /** + * Conditionally set output to x or y, depending on if mask is set or + * cleared (resp) + */ + void select_n(T output[], const T x[], const T y[], size_t len) const + { + for(size_t i = 0; i != len; ++i) + output[i] = this->select(x[i], y[i]); + } + + /** + * If this mask is set, zero out buf, otherwise do nothing + */ + void if_set_zero_out(T buf[], size_t elems) + { + for(size_t i = 0; i != elems; ++i) + { + buf[i] = this->if_not_set_return(buf[i]); + } + } + + /** + * Return the value of the mask, unpoisoned + */ + T unpoisoned_value() const + { + T r = value(); + CT::unpoison(r); + return r; + } + + /** + * Return true iff this mask is set + */ + bool is_set() const + { + return unpoisoned_value() != 0; + } + + /** + * Return the underlying value of the mask + */ + T value() const + { + return m_mask; + } + + private: + Mask(T m) : m_mask(m) {} + + T m_mask; + }; + +template<typename T> +inline Mask<T> conditional_copy_mem(T cnd, + T* to, + const T* from0, + const T* from1, + size_t elems) + { + const auto mask = CT::Mask<T>::expand(cnd); + mask.select_n(to, from0, from1, elems); + return mask; + } + +template<typename T> +inline void conditional_swap(bool cnd, T& x, T& y) + { + const auto swap = CT::Mask<T>::expand(cnd); + + T t0 = swap.select(y, x); + T t1 = swap.select(x, y); + x = t0; + y = t1; + } + +template<typename T> +inline void conditional_swap_ptr(bool cnd, T& x, T& y) + { + uintptr_t xp = reinterpret_cast<uintptr_t>(x); + uintptr_t yp = reinterpret_cast<uintptr_t>(y); + + conditional_swap<uintptr_t>(cnd, xp, yp); + + x = reinterpret_cast<T>(xp); + y = reinterpret_cast<T>(yp); + } + +/** +* If bad_mask is unset, return in[delim_idx:input_length] copied to +* new buffer. If bad_mask is set, return an all zero vector of +* unspecified length. +*/ +secure_vector<uint8_t> copy_output(CT::Mask<uint8_t> bad_input, + const uint8_t input[], + size_t input_length, + size_t delim_idx); + +secure_vector<uint8_t> strip_leading_zeros(const uint8_t in[], size_t length); + +inline secure_vector<uint8_t> strip_leading_zeros(const secure_vector<uint8_t>& in) + { + return strip_leading_zeros(in.data(), in.size()); + } + +} + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/data_src.cpp b/comm/third_party/botan/src/lib/utils/data_src.cpp new file mode 100644 index 0000000000..c5689534ff --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/data_src.cpp @@ -0,0 +1,214 @@ +/* +* DataSource +* (C) 1999-2007 Jack Lloyd +* 2005 Matthew Gregan +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/data_src.h> +#include <botan/exceptn.h> +#include <algorithm> +#include <istream> + +#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) + #include <fstream> +#endif + +namespace Botan { + +/* +* Read a single byte from the DataSource +*/ +size_t DataSource::read_byte(uint8_t& out) + { + return read(&out, 1); + } + +/* +* Peek a single byte from the DataSource +*/ +size_t DataSource::peek_byte(uint8_t& out) const + { + return peek(&out, 1, 0); + } + +/* +* Discard the next N bytes of the data +*/ +size_t DataSource::discard_next(size_t n) + { + uint8_t buf[64] = { 0 }; + size_t discarded = 0; + + while(n) + { + const size_t got = this->read(buf, std::min(n, sizeof(buf))); + discarded += got; + n -= got; + + if(got == 0) + break; + } + + return discarded; + } + +/* +* Read from a memory buffer +*/ +size_t DataSource_Memory::read(uint8_t out[], size_t length) + { + const size_t got = std::min<size_t>(m_source.size() - m_offset, length); + copy_mem(out, m_source.data() + m_offset, got); + m_offset += got; + return got; + } + +bool DataSource_Memory::check_available(size_t n) + { + return (n <= (m_source.size() - m_offset)); + } + +/* +* Peek into a memory buffer +*/ +size_t DataSource_Memory::peek(uint8_t out[], size_t length, + size_t peek_offset) const + { + const size_t bytes_left = m_source.size() - m_offset; + if(peek_offset >= bytes_left) return 0; + + const size_t got = std::min(bytes_left - peek_offset, length); + copy_mem(out, &m_source[m_offset + peek_offset], got); + return got; + } + +/* +* Check if the memory buffer is empty +*/ +bool DataSource_Memory::end_of_data() const + { + return (m_offset == m_source.size()); + } + +/* +* DataSource_Memory Constructor +*/ +DataSource_Memory::DataSource_Memory(const std::string& in) : + m_source(cast_char_ptr_to_uint8(in.data()), + cast_char_ptr_to_uint8(in.data()) + in.length()), + m_offset(0) + { + } + +/* +* Read from a stream +*/ +size_t DataSource_Stream::read(uint8_t out[], size_t length) + { + m_source.read(cast_uint8_ptr_to_char(out), length); + if(m_source.bad()) + throw Stream_IO_Error("DataSource_Stream::read: Source failure"); + + const size_t got = static_cast<size_t>(m_source.gcount()); + m_total_read += got; + return got; + } + +bool DataSource_Stream::check_available(size_t n) + { + const std::streampos orig_pos = m_source.tellg(); + m_source.seekg(0, std::ios::end); + const size_t avail = static_cast<size_t>(m_source.tellg() - orig_pos); + m_source.seekg(orig_pos); + return (avail >= n); + } + +/* +* Peek into a stream +*/ +size_t DataSource_Stream::peek(uint8_t out[], size_t length, size_t offset) const + { + if(end_of_data()) + throw Invalid_State("DataSource_Stream: Cannot peek when out of data"); + + size_t got = 0; + + if(offset) + { + secure_vector<uint8_t> buf(offset); + m_source.read(cast_uint8_ptr_to_char(buf.data()), buf.size()); + if(m_source.bad()) + throw Stream_IO_Error("DataSource_Stream::peek: Source failure"); + got = static_cast<size_t>(m_source.gcount()); + } + + if(got == offset) + { + m_source.read(cast_uint8_ptr_to_char(out), length); + if(m_source.bad()) + throw Stream_IO_Error("DataSource_Stream::peek: Source failure"); + got = static_cast<size_t>(m_source.gcount()); + } + + if(m_source.eof()) + m_source.clear(); + m_source.seekg(m_total_read, std::ios::beg); + + return got; + } + +/* +* Check if the stream is empty or in error +*/ +bool DataSource_Stream::end_of_data() const + { + return (!m_source.good()); + } + +/* +* Return a human-readable ID for this stream +*/ +std::string DataSource_Stream::id() const + { + return m_identifier; + } + +#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) + +/* +* DataSource_Stream Constructor +*/ +DataSource_Stream::DataSource_Stream(const std::string& path, + bool use_binary) : + m_identifier(path), + m_source_memory(new std::ifstream(path, use_binary ? std::ios::binary : std::ios::in)), + m_source(*m_source_memory), + m_total_read(0) + { + if(!m_source.good()) + { + throw Stream_IO_Error("DataSource: Failure opening file " + path); + } + } + +#endif + +/* +* DataSource_Stream Constructor +*/ +DataSource_Stream::DataSource_Stream(std::istream& in, + const std::string& name) : + m_identifier(name), + m_source(in), + m_total_read(0) + { + } + +DataSource_Stream::~DataSource_Stream() + { + // for ~unique_ptr + } + +} diff --git a/comm/third_party/botan/src/lib/utils/data_src.h b/comm/third_party/botan/src/lib/utils/data_src.h new file mode 100644 index 0000000000..09c1bffdf7 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/data_src.h @@ -0,0 +1,181 @@ +/* +* DataSource +* (C) 1999-2007 Jack Lloyd +* 2012 Markus Wanner +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_DATA_SRC_H_ +#define BOTAN_DATA_SRC_H_ + +#include <botan/secmem.h> +#include <string> +#include <iosfwd> + +namespace Botan { + +/** +* This class represents an abstract data source object. +*/ +class BOTAN_PUBLIC_API(2,0) DataSource + { + public: + /** + * Read from the source. Moves the internal offset so that every + * call to read will return a new portion of the source. + * + * @param out the byte array to write the result to + * @param length the length of the byte array out + * @return length in bytes that was actually read and put + * into out + */ + virtual size_t read(uint8_t out[], size_t length) BOTAN_WARN_UNUSED_RESULT = 0; + + virtual bool check_available(size_t n) = 0; + + /** + * Read from the source but do not modify the internal + * offset. Consecutive calls to peek() will return portions of + * the source starting at the same position. + * + * @param out the byte array to write the output to + * @param length the length of the byte array out + * @param peek_offset the offset into the stream to read at + * @return length in bytes that was actually read and put + * into out + */ + virtual size_t peek(uint8_t out[], size_t length, size_t peek_offset) const BOTAN_WARN_UNUSED_RESULT = 0; + + /** + * Test whether the source still has data that can be read. + * @return true if there is no more data to read, false otherwise + */ + virtual bool end_of_data() const = 0; + /** + * return the id of this data source + * @return std::string representing the id of this data source + */ + virtual std::string id() const { return ""; } + + /** + * Read one byte. + * @param out the byte to read to + * @return length in bytes that was actually read and put + * into out + */ + size_t read_byte(uint8_t& out); + + /** + * Peek at one byte. + * @param out an output byte + * @return length in bytes that was actually read and put + * into out + */ + size_t peek_byte(uint8_t& out) const; + + /** + * Discard the next N bytes of the data + * @param N the number of bytes to discard + * @return number of bytes actually discarded + */ + size_t discard_next(size_t N); + + /** + * @return number of bytes read so far. + */ + virtual size_t get_bytes_read() const = 0; + + DataSource() = default; + virtual ~DataSource() = default; + DataSource& operator=(const DataSource&) = delete; + DataSource(const DataSource&) = delete; + }; + +/** +* This class represents a Memory-Based DataSource +*/ +class BOTAN_PUBLIC_API(2,0) DataSource_Memory final : public DataSource + { + public: + size_t read(uint8_t[], size_t) override; + size_t peek(uint8_t[], size_t, size_t) const override; + bool check_available(size_t n) override; + bool end_of_data() const override; + + /** + * Construct a memory source that reads from a string + * @param in the string to read from + */ + explicit DataSource_Memory(const std::string& in); + + /** + * Construct a memory source that reads from a byte array + * @param in the byte array to read from + * @param length the length of the byte array + */ + DataSource_Memory(const uint8_t in[], size_t length) : + m_source(in, in + length), m_offset(0) {} + + /** + * Construct a memory source that reads from a secure_vector + * @param in the MemoryRegion to read from + */ + explicit DataSource_Memory(const secure_vector<uint8_t>& in) : + m_source(in), m_offset(0) {} + + /** + * Construct a memory source that reads from a std::vector + * @param in the MemoryRegion to read from + */ + explicit DataSource_Memory(const std::vector<uint8_t>& in) : + m_source(in.begin(), in.end()), m_offset(0) {} + + size_t get_bytes_read() const override { return m_offset; } + private: + secure_vector<uint8_t> m_source; + size_t m_offset; + }; + +/** +* This class represents a Stream-Based DataSource. +*/ +class BOTAN_PUBLIC_API(2,0) DataSource_Stream final : public DataSource + { + public: + size_t read(uint8_t[], size_t) override; + size_t peek(uint8_t[], size_t, size_t) const override; + bool check_available(size_t n) override; + bool end_of_data() const override; + std::string id() const override; + + DataSource_Stream(std::istream&, + const std::string& id = "<std::istream>"); + +#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) + /** + * Construct a Stream-Based DataSource from filesystem path + * @param file the path to the file + * @param use_binary whether to treat the file as binary or not + */ + DataSource_Stream(const std::string& file, bool use_binary = false); +#endif + + DataSource_Stream(const DataSource_Stream&) = delete; + + DataSource_Stream& operator=(const DataSource_Stream&) = delete; + + ~DataSource_Stream(); + + size_t get_bytes_read() const override { return m_total_read; } + private: + const std::string m_identifier; + + std::unique_ptr<std::istream> m_source_memory; + std::istream& m_source; + size_t m_total_read; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/database.h b/comm/third_party/botan/src/lib/utils/database.h new file mode 100644 index 0000000000..713d4fc59c --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/database.h @@ -0,0 +1,88 @@ +/* +* SQL database interface +* (C) 2014 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SQL_DATABASE_H_ +#define BOTAN_SQL_DATABASE_H_ + +#include <botan/types.h> +#include <botan/exceptn.h> +#include <string> +#include <chrono> +#include <vector> + +namespace Botan { + +class BOTAN_PUBLIC_API(2,0) SQL_Database + { + public: + + class BOTAN_PUBLIC_API(2,0) SQL_DB_Error final : public Exception + { + public: + explicit SQL_DB_Error(const std::string& what) : + Exception("SQL database", what), + m_rc(0) + {} + + SQL_DB_Error(const std::string& what, int rc) : + Exception("SQL database", what), + m_rc(rc) + {} + + ErrorType error_type() const noexcept override { return Botan::ErrorType::DatabaseError; } + + int error_code() const noexcept override { return m_rc; } + private: + int m_rc; + }; + + class BOTAN_PUBLIC_API(2,0) Statement + { + public: + /* Bind statement parameters */ + virtual void bind(int column, const std::string& str) = 0; + + virtual void bind(int column, size_t i) = 0; + + virtual void bind(int column, std::chrono::system_clock::time_point time) = 0; + + virtual void bind(int column, const std::vector<uint8_t>& blob) = 0; + + virtual void bind(int column, const uint8_t* data, size_t len) = 0; + + /* Get output */ + virtual std::pair<const uint8_t*, size_t> get_blob(int column) = 0; + + virtual std::string get_str(int column) = 0; + + virtual size_t get_size_t(int column) = 0; + + /* Run to completion */ + virtual size_t spin() = 0; + + /* Maybe update */ + virtual bool step() = 0; + + virtual ~Statement() = default; + }; + + /* + * Create a new statement for execution. + * Use ?1, ?2, ?3, etc for parameters to set later with bind + */ + virtual std::shared_ptr<Statement> new_statement(const std::string& base_sql) const = 0; + + virtual size_t row_count(const std::string& table_name) = 0; + + virtual void create_table(const std::string& table_schema) = 0; + + virtual ~SQL_Database() = default; +}; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/donna128.h b/comm/third_party/botan/src/lib/utils/donna128.h new file mode 100644 index 0000000000..ff571906d8 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/donna128.h @@ -0,0 +1,143 @@ +/* +* A minimal 128-bit integer type for curve25519-donna +* (C) 2014 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CURVE25519_DONNA128_H_ +#define BOTAN_CURVE25519_DONNA128_H_ + +#include <botan/mul128.h> + +namespace Botan { + +class donna128 final + { + public: + donna128(uint64_t ll = 0, uint64_t hh = 0) { l = ll; h = hh; } + + donna128(const donna128&) = default; + donna128& operator=(const donna128&) = default; + + friend donna128 operator>>(const donna128& x, size_t shift) + { + donna128 z = x; + if(shift > 0) + { + const uint64_t carry = z.h << (64 - shift); + z.h = (z.h >> shift); + z.l = (z.l >> shift) | carry; + } + return z; + } + + friend donna128 operator<<(const donna128& x, size_t shift) + { + donna128 z = x; + if(shift > 0) + { + const uint64_t carry = z.l >> (64 - shift); + z.l = (z.l << shift); + z.h = (z.h << shift) | carry; + } + return z; + } + + friend uint64_t operator&(const donna128& x, uint64_t mask) + { + return x.l & mask; + } + + uint64_t operator&=(uint64_t mask) + { + h = 0; + l &= mask; + return l; + } + + donna128& operator+=(const donna128& x) + { + l += x.l; + h += x.h; + + const uint64_t carry = (l < x.l); + h += carry; + return *this; + } + + donna128& operator+=(uint64_t x) + { + l += x; + const uint64_t carry = (l < x); + h += carry; + return *this; + } + + uint64_t lo() const { return l; } + uint64_t hi() const { return h; } + private: + uint64_t h = 0, l = 0; + }; + +inline donna128 operator*(const donna128& x, uint64_t y) + { + BOTAN_ARG_CHECK(x.hi() == 0, "High 64 bits of donna128 set to zero during multiply"); + + uint64_t lo = 0, hi = 0; + mul64x64_128(x.lo(), y, &lo, &hi); + return donna128(lo, hi); + } + +inline donna128 operator*(uint64_t y, const donna128& x) + { + return x * y; + } + +inline donna128 operator+(const donna128& x, const donna128& y) + { + donna128 z = x; + z += y; + return z; + } + +inline donna128 operator+(const donna128& x, uint64_t y) + { + donna128 z = x; + z += y; + return z; + } + +inline donna128 operator|(const donna128& x, const donna128& y) + { + return donna128(x.lo() | y.lo(), x.hi() | y.hi()); + } + +inline uint64_t carry_shift(const donna128& a, size_t shift) + { + return (a >> shift).lo(); + } + +inline uint64_t combine_lower(const donna128& a, size_t s1, + const donna128& b, size_t s2) + { + donna128 z = (a >> s1) | (b << s2); + return z.lo(); + } + +#if defined(BOTAN_TARGET_HAS_NATIVE_UINT128) +inline uint64_t carry_shift(const uint128_t a, size_t shift) + { + return static_cast<uint64_t>(a >> shift); + } + +inline uint64_t combine_lower(const uint128_t a, size_t s1, + const uint128_t b, size_t s2) + { + return static_cast<uint64_t>((a >> s1) | (b << s2)); + } +#endif + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.cpp b/comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.cpp new file mode 100644 index 0000000000..b32fe4b3ab --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.cpp @@ -0,0 +1,82 @@ +/* +* Dynamically Loaded Object +* (C) 2010 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/dyn_load.h> +#include <botan/exceptn.h> + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + #include <dlfcn.h> +#elif defined(BOTAN_TARGET_OS_HAS_WIN32) + #define NOMINMAX 1 + #define _WINSOCKAPI_ // stop windows.h including winsock.h + #include <windows.h> +#endif + +namespace Botan { + +namespace { + +void raise_runtime_loader_exception(const std::string& lib_name, + const char* msg) + { + const std::string ex_msg = + "Failed to load " + lib_name + ": " + + (msg ? msg : "Unknown error"); + + throw System_Error(ex_msg, 0); + } + +} + +Dynamically_Loaded_Library::Dynamically_Loaded_Library( + const std::string& library) : + m_lib_name(library), m_lib(nullptr) + { +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + m_lib = ::dlopen(m_lib_name.c_str(), RTLD_LAZY); + + if(!m_lib) + raise_runtime_loader_exception(m_lib_name, ::dlerror()); + +#elif defined(BOTAN_TARGET_OS_HAS_WIN32) + m_lib = ::LoadLibraryA(m_lib_name.c_str()); + + if(!m_lib) + raise_runtime_loader_exception(m_lib_name, "LoadLibrary failed"); +#endif + + if(!m_lib) + raise_runtime_loader_exception(m_lib_name, "Dynamic load not supported"); + } + +Dynamically_Loaded_Library::~Dynamically_Loaded_Library() + { +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + ::dlclose(m_lib); +#elif defined(BOTAN_TARGET_OS_HAS_WIN32) + ::FreeLibrary((HMODULE)m_lib); +#endif + } + +void* Dynamically_Loaded_Library::resolve_symbol(const std::string& symbol) + { + void* addr = nullptr; + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + addr = ::dlsym(m_lib, symbol.c_str()); +#elif defined(BOTAN_TARGET_OS_HAS_WIN32) + addr = reinterpret_cast<void*>(::GetProcAddress((HMODULE)m_lib, symbol.c_str())); +#endif + + if(!addr) + throw Invalid_Argument("Failed to resolve symbol " + symbol + + " in " + m_lib_name); + + return addr; + } + +} diff --git a/comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.h b/comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.h new file mode 100644 index 0000000000..3caf65f277 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.h @@ -0,0 +1,68 @@ +/* +* Dynamically Loaded Object +* (C) 2010 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_DYNAMIC_LOADER_H_ +#define BOTAN_DYNAMIC_LOADER_H_ + +#include <botan/types.h> +#include <string> + +namespace Botan { + +/** +* Represents a DLL or shared object +*/ +class BOTAN_PUBLIC_API(2,0) Dynamically_Loaded_Library final + { + public: + /** + * Load a DLL (or fail with an exception) + * @param lib_name name or path to a library + * + * If you don't use a full path, the search order will be defined + * by whatever the system linker does by default. Always using fully + * qualified pathnames can help prevent code injection attacks (eg + * via manipulation of LD_LIBRARY_PATH on Linux) + */ + Dynamically_Loaded_Library(const std::string& lib_name); + + /** + * Unload the DLL + * @warning Any pointers returned by resolve()/resolve_symbol() + * should not be used after this destructor runs. + */ + ~Dynamically_Loaded_Library(); + + /** + * Load a symbol (or fail with an exception) + * @param symbol names the symbol to load + * @return address of the loaded symbol + */ + void* resolve_symbol(const std::string& symbol); + + /** + * Convenience function for casting symbol to the right type + * @param symbol names the symbol to load + * @return address of the loaded symbol + */ + template<typename T> + T resolve(const std::string& symbol) + { + return reinterpret_cast<T>(resolve_symbol(symbol)); + } + + private: + Dynamically_Loaded_Library(const Dynamically_Loaded_Library&); + Dynamically_Loaded_Library& operator=(const Dynamically_Loaded_Library&); + + std::string m_lib_name; + void* m_lib; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/dyn_load/info.txt b/comm/third_party/botan/src/lib/utils/dyn_load/info.txt new file mode 100644 index 0000000000..f20d000246 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/dyn_load/info.txt @@ -0,0 +1,18 @@ +<defines> +DYNAMIC_LOADER -> 20160310 +</defines> + +load_on dep + +<os_features> +posix1 +win32 +</os_features> + +<libs> +android -> dl +linux -> dl +solaris -> dl +macos -> dl +hurd -> dl +</libs> diff --git a/comm/third_party/botan/src/lib/utils/exceptn.cpp b/comm/third_party/botan/src/lib/utils/exceptn.cpp new file mode 100644 index 0000000000..e6d067f348 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/exceptn.cpp @@ -0,0 +1,183 @@ +/* +* (C) 2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/exceptn.h> + +namespace Botan { + +std::string to_string(ErrorType type) + { + switch(type) + { + case ErrorType::Unknown: + return "Unknown"; + case ErrorType::SystemError: + return "SystemError"; + case ErrorType::NotImplemented: + return "NotImplemented"; + case ErrorType::OutOfMemory: + return "OutOfMemory"; + case ErrorType::InternalError: + return "InternalError"; + case ErrorType::IoError: + return "IoError"; + case ErrorType::InvalidObjectState : + return "InvalidObjectState"; + case ErrorType::KeyNotSet: + return "KeyNotSet"; + case ErrorType::InvalidArgument: + return "InvalidArgument"; + case ErrorType::InvalidKeyLength: + return "InvalidKeyLength"; + case ErrorType::InvalidNonceLength: + return "InvalidNonceLength"; + case ErrorType::LookupError: + return "LookupError"; + case ErrorType::EncodingFailure: + return "EncodingFailure"; + case ErrorType::DecodingFailure: + return "DecodingFailure"; + case ErrorType::TLSError: + return "TLSError"; + case ErrorType::HttpError: + return "HttpError"; + case ErrorType::InvalidTag: + return "InvalidTag"; + case ErrorType::RoughtimeError: + return "RoughtimeError"; + case ErrorType::OpenSSLError : + return "OpenSSLError"; + case ErrorType::CommonCryptoError: + return "CommonCryptoError"; + case ErrorType::Pkcs11Error: + return "Pkcs11Error"; + case ErrorType::TPMError: + return "TPMError"; + case ErrorType::DatabaseError: + return "DatabaseError"; + case ErrorType::ZlibError : + return "ZlibError"; + case ErrorType::Bzip2Error: + return "Bzip2Error" ; + case ErrorType::LzmaError: + return "LzmaError"; + } + + // No default case in above switch so compiler warns + return "Unrecognized Botan error"; + } + +Exception::Exception(const std::string& msg) : m_msg(msg) + {} + +Exception::Exception(const std::string& msg, const std::exception& e) : + m_msg(msg + " failed with " + std::string(e.what())) + {} + +Exception::Exception(const char* prefix, const std::string& msg) : + m_msg(std::string(prefix) + " " + msg) + {} + +Invalid_Argument::Invalid_Argument(const std::string& msg) : + Exception(msg) + {} + +Invalid_Argument::Invalid_Argument(const std::string& msg, const std::string& where) : + Exception(msg + " in " + where) + {} + +Invalid_Argument::Invalid_Argument(const std::string& msg, const std::exception& e) : + Exception(msg, e) {} + +Lookup_Error::Lookup_Error(const std::string& type, + const std::string& algo, + const std::string& provider) : + Exception("Unavailable " + type + " " + algo + + (provider.empty() ? std::string("") : (" for provider " + provider))) + {} + +Internal_Error::Internal_Error(const std::string& err) : + Exception("Internal error: " + err) + {} + +Invalid_Key_Length::Invalid_Key_Length(const std::string& name, size_t length) : + Invalid_Argument(name + " cannot accept a key of length " + + std::to_string(length)) + {} + +Invalid_IV_Length::Invalid_IV_Length(const std::string& mode, size_t bad_len) : + Invalid_Argument("IV length " + std::to_string(bad_len) + + " is invalid for " + mode) + {} + +Key_Not_Set::Key_Not_Set(const std::string& algo) : + Invalid_State("Key not set in " + algo) + {} + +Policy_Violation::Policy_Violation(const std::string& err) : + Invalid_State("Policy violation: " + err) {} + +PRNG_Unseeded::PRNG_Unseeded(const std::string& algo) : + Invalid_State("PRNG not seeded: " + algo) + {} + +Algorithm_Not_Found::Algorithm_Not_Found(const std::string& name) : + Lookup_Error("Could not find any algorithm named \"" + name + "\"") + {} + +No_Provider_Found::No_Provider_Found(const std::string& name) : + Exception("Could not find any provider for algorithm named \"" + name + "\"") + {} + +Provider_Not_Found::Provider_Not_Found(const std::string& algo, const std::string& provider) : + Lookup_Error("Could not find provider '" + provider + "' for " + algo) + {} + +Invalid_Algorithm_Name::Invalid_Algorithm_Name(const std::string& name): + Invalid_Argument("Invalid algorithm name: " + name) + {} + +Encoding_Error::Encoding_Error(const std::string& name) : + Invalid_Argument("Encoding error: " + name) + {} + +Decoding_Error::Decoding_Error(const std::string& name) : + Invalid_Argument(name) + {} + +Decoding_Error::Decoding_Error(const std::string& msg, const std::exception& e) : + Invalid_Argument(msg, e) + {} + +Decoding_Error::Decoding_Error(const std::string& name, const char* exception_message) : + Invalid_Argument(name + " failed with exception " + exception_message) {} + +Invalid_Authentication_Tag::Invalid_Authentication_Tag(const std::string& msg) : + Exception("Invalid authentication tag: " + msg) + {} + +Invalid_OID::Invalid_OID(const std::string& oid) : + Decoding_Error("Invalid ASN.1 OID: " + oid) + {} + +Stream_IO_Error::Stream_IO_Error(const std::string& err) : + Exception("I/O error: " + err) + {} + +System_Error::System_Error(const std::string& msg, int err_code) : + Exception(msg + " error code " + std::to_string(err_code)), + m_error_code(err_code) + {} + +Self_Test_Failure::Self_Test_Failure(const std::string& err) : + Internal_Error("Self test failed: " + err) + {} + +Not_Implemented::Not_Implemented(const std::string& err) : + Exception("Not implemented", err) + {} + +} diff --git a/comm/third_party/botan/src/lib/utils/exceptn.h b/comm/third_party/botan/src/lib/utils/exceptn.h new file mode 100644 index 0000000000..442ec91e6e --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/exceptn.h @@ -0,0 +1,441 @@ +/* +* Exceptions +* (C) 1999-2009,2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_EXCEPTION_H_ +#define BOTAN_EXCEPTION_H_ + +#include <botan/types.h> +#include <exception> +#include <string> + +namespace Botan { + +/** +* Different types of errors that might occur +*/ +enum class ErrorType { + /** Some unknown error */ + Unknown = 1, + /** An error while calling a system interface */ + SystemError, + /** An operation seems valid, but not supported by the current version */ + NotImplemented, + /** Memory allocation failure */ + OutOfMemory, + /** An internal error occurred */ + InternalError, + /** An I/O error occurred */ + IoError, + + /** Invalid object state */ + InvalidObjectState = 100, + /** A key was not set on an object when this is required */ + KeyNotSet, + /** The application provided an argument which is invalid */ + InvalidArgument, + /** A key with invalid length was provided */ + InvalidKeyLength, + /** A nonce with invalid length was provided */ + InvalidNonceLength, + /** An object type was requested but cannot be found */ + LookupError, + /** Encoding a message or datum failed */ + EncodingFailure, + /** Decoding a message or datum failed */ + DecodingFailure, + /** A TLS error (error_code will be the alert type) */ + TLSError, + /** An error during an HTTP operation */ + HttpError, + /** A message with an invalid authentication tag was detected */ + InvalidTag, + /** An error during Roughtime validation */ + RoughtimeError, + + /** An error when calling OpenSSL */ + OpenSSLError = 200, + /** An error when interacting with CommonCrypto API */ + CommonCryptoError, + /** An error when interacting with a PKCS11 device */ + Pkcs11Error, + /** An error when interacting with a TPM device */ + TPMError, + /** An error when interacting with a database */ + DatabaseError, + + /** An error when interacting with zlib */ + ZlibError = 300, + /** An error when interacting with bzip2 */ + Bzip2Error, + /** An error when interacting with lzma */ + LzmaError, + +}; + +//! \brief Convert an ErrorType to string +std::string BOTAN_PUBLIC_API(2,11) to_string(ErrorType type); + +/** +* Base class for all exceptions thrown by the library +*/ +class BOTAN_PUBLIC_API(2,0) Exception : public std::exception + { + public: + /** + * Return a descriptive string which is hopefully comprehensible to + * a developer. It will likely not be useful for an end user. + * + * The string has no particular format, and the content of exception + * messages may change from release to release. Thus the main use of this + * function is for logging or debugging. + */ + const char* what() const noexcept override { return m_msg.c_str(); } + + /** + * Return the "type" of error which occurred. + */ + virtual ErrorType error_type() const noexcept { return Botan::ErrorType::Unknown; } + + /** + * Return an error code associated with this exception, or otherwise 0. + * + * The domain of this error varies depending on the source, for example on + * POSIX systems it might be errno, while on a Windows system it might be + * the result of GetLastError or WSAGetLastError. For error_type() is + * OpenSSLError, it will (if nonzero) be an OpenSSL error code from + * ERR_get_error. + */ + virtual int error_code() const noexcept { return 0; } + + /** + * Avoid throwing base Exception, use a subclass + */ + explicit Exception(const std::string& msg); + + /** + * Avoid throwing base Exception, use a subclass + */ + Exception(const char* prefix, const std::string& msg); + + /** + * Avoid throwing base Exception, use a subclass + */ + Exception(const std::string& msg, const std::exception& e); + + private: + std::string m_msg; + }; + +/** +* An invalid argument was provided to an API call. +*/ +class BOTAN_PUBLIC_API(2,0) Invalid_Argument : public Exception + { + public: + explicit Invalid_Argument(const std::string& msg); + + explicit Invalid_Argument(const std::string& msg, const std::string& where); + + Invalid_Argument(const std::string& msg, const std::exception& e); + + ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; } + }; + +/** +* An invalid key length was used +*/ +class BOTAN_PUBLIC_API(2,0) Invalid_Key_Length final : public Invalid_Argument + { + public: + Invalid_Key_Length(const std::string& name, size_t length); + ErrorType error_type() const noexcept override { return ErrorType::InvalidKeyLength; } + }; + +/** +* An invalid nonce length was used +*/ +class BOTAN_PUBLIC_API(2,0) Invalid_IV_Length final : public Invalid_Argument + { + public: + Invalid_IV_Length(const std::string& mode, size_t bad_len); + ErrorType error_type() const noexcept override { return ErrorType::InvalidNonceLength; } + }; + +/** +* Invalid_Algorithm_Name Exception +*/ +class BOTAN_PUBLIC_API(2,0) Invalid_Algorithm_Name final : public Invalid_Argument + { + public: + explicit Invalid_Algorithm_Name(const std::string& name); + }; + +/** +* Encoding_Error Exception +* +* This exception derives from Invalid_Argument for historical reasons, and it +* does not make any real sense for it to do so. In a future major release this +* exception type will derive directly from Exception instead. +*/ +class BOTAN_PUBLIC_API(2,0) Encoding_Error final : public Invalid_Argument + { + public: + explicit Encoding_Error(const std::string& name); + + ErrorType error_type() const noexcept override { return ErrorType::EncodingFailure; } + }; + +/** +* A decoding error occurred. +* +* This exception derives from Invalid_Argument for historical reasons, and it +* does not make any real sense for it to do so. In a future major release this +* exception type will derive directly from Exception instead. +*/ +class BOTAN_PUBLIC_API(2,0) Decoding_Error : public Invalid_Argument + { + public: + explicit Decoding_Error(const std::string& name); + + Decoding_Error(const std::string& name, const char* exception_message); + + Decoding_Error(const std::string& msg, const std::exception& e); + + ErrorType error_type() const noexcept override { return ErrorType::DecodingFailure; } + }; + +/** +* Invalid state was encountered. A request was made on an object while the +* object was in a state where the operation cannot be performed. +*/ +class BOTAN_PUBLIC_API(2,0) Invalid_State : public Exception + { + public: + explicit Invalid_State(const std::string& err) : Exception(err) {} + + ErrorType error_type() const noexcept override { return ErrorType::InvalidObjectState; } + }; + +/** +* A PRNG was called on to produce output while still unseeded +*/ +class BOTAN_PUBLIC_API(2,0) PRNG_Unseeded final : public Invalid_State + { + public: + explicit PRNG_Unseeded(const std::string& algo); + }; + +/** +* The key was not set on an object. This occurs with symmetric objects where +* an operation which requires the key is called prior to set_key being called. +*/ +class BOTAN_PUBLIC_API(2,4) Key_Not_Set : public Invalid_State + { + public: + explicit Key_Not_Set(const std::string& algo); + + ErrorType error_type() const noexcept override { return ErrorType::KeyNotSet; } + }; + +/** +* A request was made for some kind of object which could not be located +*/ +class BOTAN_PUBLIC_API(2,0) Lookup_Error : public Exception + { + public: + explicit Lookup_Error(const std::string& err) : Exception(err) {} + + Lookup_Error(const std::string& type, + const std::string& algo, + const std::string& provider); + + ErrorType error_type() const noexcept override { return ErrorType::LookupError; } + }; + +/** +* Algorithm_Not_Found Exception +* +* @warning This exception type will be removed in the future. Instead +* just catch Lookup_Error. +*/ +class BOTAN_PUBLIC_API(2,0) Algorithm_Not_Found final : public Lookup_Error + { + public: + explicit Algorithm_Not_Found(const std::string& name); + }; + +/** +* Provider_Not_Found is thrown when a specific provider was requested +* but that provider is not available. +* +* @warning This exception type will be removed in the future. Instead +* just catch Lookup_Error. +*/ +class BOTAN_PUBLIC_API(2,0) Provider_Not_Found final : public Lookup_Error + { + public: + Provider_Not_Found(const std::string& algo, const std::string& provider); + }; + +/** +* An AEAD or MAC check detected a message modification +* +* In versions before 2.10, Invalid_Authentication_Tag was named +* Integrity_Failure, it was renamed to make its usage more clear. +*/ +class BOTAN_PUBLIC_API(2,0) Invalid_Authentication_Tag final : public Exception + { + public: + explicit Invalid_Authentication_Tag(const std::string& msg); + + ErrorType error_type() const noexcept override { return ErrorType::InvalidTag; } + }; + +/** +* For compatability with older versions +*/ +typedef Invalid_Authentication_Tag Integrity_Failure; + +/** +* An error occurred while operating on an IO stream +*/ +class BOTAN_PUBLIC_API(2,0) Stream_IO_Error final : public Exception + { + public: + explicit Stream_IO_Error(const std::string& err); + + ErrorType error_type() const noexcept override { return ErrorType::IoError; } + }; + +/** +* System_Error +* +* This exception is thrown in the event of an error related to interacting +* with the operating system. +* +* This exception type also (optionally) captures an integer error code eg +* POSIX errno or Windows GetLastError. +*/ +class BOTAN_PUBLIC_API(2,9) System_Error : public Exception + { + public: + System_Error(const std::string& msg) : Exception(msg), m_error_code(0) {} + + System_Error(const std::string& msg, int err_code); + + ErrorType error_type() const noexcept override { return ErrorType::SystemError; } + + int error_code() const noexcept override { return m_error_code; } + + private: + int m_error_code; + }; + +/** +* An internal error occurred. If observed, please file a bug. +*/ +class BOTAN_PUBLIC_API(2,0) Internal_Error : public Exception + { + public: + explicit Internal_Error(const std::string& err); + + ErrorType error_type() const noexcept override { return ErrorType::InternalError; } + }; + +/** +* Not Implemented Exception +* +* This is thrown in the situation where a requested operation is +* logically valid but is not implemented by this version of the library. +*/ +class BOTAN_PUBLIC_API(2,0) Not_Implemented final : public Exception + { + public: + explicit Not_Implemented(const std::string& err); + + ErrorType error_type() const noexcept override { return ErrorType::NotImplemented; } + }; + +/* + The following exception types are still in use for compatability reasons, + but are deprecated and will be removed in a future major release. + Instead catch the base class. +*/ + +/** +* An invalid OID string was used. +* +* This exception will be removed in a future major release. +*/ +class BOTAN_PUBLIC_API(2,0) Invalid_OID final : public Decoding_Error + { + public: + explicit Invalid_OID(const std::string& oid); + }; + +/* + The following exception types are deprecated, no longer used, + and will be removed in a future major release +*/ + +/** +* Self Test Failure Exception +* +* This exception is no longer used. It will be removed in a future major release. +*/ +class BOTAN_PUBLIC_API(2,0) Self_Test_Failure final : public Internal_Error + { + public: + BOTAN_DEPRECATED("no longer used") explicit Self_Test_Failure(const std::string& err); + }; + +/** +* No_Provider_Found Exception +* +* This exception is no longer used. It will be removed in a future major release. +*/ +class BOTAN_PUBLIC_API(2,0) No_Provider_Found final : public Exception + { + public: + BOTAN_DEPRECATED("no longer used") explicit No_Provider_Found(const std::string& name); + }; + +/** +* Policy_Violation Exception +* +* This exception is no longer used. It will be removed in a future major release. +*/ +class BOTAN_PUBLIC_API(2,0) Policy_Violation final : public Invalid_State + { + public: + BOTAN_DEPRECATED("no longer used") explicit Policy_Violation(const std::string& err); + }; + +/** +* Unsupported_Argument Exception +* +* An argument that is invalid because it is not supported by Botan. +* It might or might not be valid in another context like a standard. +* +* This exception is no longer used, instead Not_Implemented is thrown. +* It will be removed in a future major release. +*/ +class BOTAN_PUBLIC_API(2,0) Unsupported_Argument final : public Invalid_Argument + { + public: + BOTAN_DEPRECATED("no longer used") explicit Unsupported_Argument(const std::string& msg) : Invalid_Argument(msg) {} + }; + +template<typename E, typename... Args> +inline void do_throw_error(const char* file, int line, const char* func, Args... args) + { + throw E(file, line, func, args...); + } + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/filesystem.cpp b/comm/third_party/botan/src/lib/utils/filesystem.cpp new file mode 100644 index 0000000000..3072a304f2 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/filesystem.cpp @@ -0,0 +1,144 @@ +/* +* (C) 2015,2017,2019 Jack Lloyd +* (C) 2015 Simon Warta (Kullo GmbH) +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/exceptn.h> +#include <botan/internal/filesystem.h> +#include <algorithm> +#include <deque> +#include <memory> + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + #include <sys/types.h> + #include <sys/stat.h> + #include <dirent.h> + #include <functional> +#elif defined(BOTAN_TARGET_OS_HAS_WIN32) + #define NOMINMAX 1 + #define _WINSOCKAPI_ // stop windows.h including winsock.h + #include <windows.h> +#endif + +namespace Botan { + +namespace { + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + +std::vector<std::string> impl_readdir(const std::string& dir_path) + { + std::vector<std::string> out; + std::deque<std::string> dir_list; + dir_list.push_back(dir_path); + + while(!dir_list.empty()) + { + const std::string cur_path = dir_list[0]; + dir_list.pop_front(); + + std::unique_ptr<DIR, std::function<int (DIR*)>> dir(::opendir(cur_path.c_str()), ::closedir); + + if(dir) + { + while(struct dirent* dirent = ::readdir(dir.get())) + { + const std::string filename = dirent->d_name; + if(filename == "." || filename == "..") + continue; + const std::string full_path = cur_path + "/" + filename; + + struct stat stat_buf; + + if(::stat(full_path.c_str(), &stat_buf) == -1) + continue; + + if(S_ISDIR(stat_buf.st_mode)) + dir_list.push_back(full_path); + else if(S_ISREG(stat_buf.st_mode)) + out.push_back(full_path); + } + } + } + + return out; + } + +#elif defined(BOTAN_TARGET_OS_HAS_WIN32) + +std::vector<std::string> impl_win32(const std::string& dir_path) + { + std::vector<std::string> out; + std::deque<std::string> dir_list; + dir_list.push_back(dir_path); + + while(!dir_list.empty()) + { + const std::string cur_path = dir_list[0]; + dir_list.pop_front(); + + WIN32_FIND_DATAA find_data; + HANDLE dir = ::FindFirstFileA((cur_path + "/*").c_str(), &find_data); + + if(dir != INVALID_HANDLE_VALUE) + { + do + { + const std::string filename = find_data.cFileName; + if(filename == "." || filename == "..") + continue; + const std::string full_path = cur_path + "/" + filename; + + if(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + dir_list.push_back(full_path); + } + else + { + out.push_back(full_path); + } + } + while(::FindNextFileA(dir, &find_data)); + } + + ::FindClose(dir); + } + + return out; +} +#endif + +} + +bool has_filesystem_impl() + { +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + return true; +#elif defined(BOTAN_TARGET_OS_HAS_WIN32) + return true; +#else + return false; +#endif + } + +std::vector<std::string> get_files_recursive(const std::string& dir) + { + std::vector<std::string> files; + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + files = impl_readdir(dir); +#elif defined(BOTAN_TARGET_OS_HAS_WIN32) + files = impl_win32(dir); +#else + BOTAN_UNUSED(dir); + throw No_Filesystem_Access(); +#endif + + std::sort(files.begin(), files.end()); + + return files; + } + +} diff --git a/comm/third_party/botan/src/lib/utils/filesystem.h b/comm/third_party/botan/src/lib/utils/filesystem.h new file mode 100644 index 0000000000..382da7de34 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/filesystem.h @@ -0,0 +1,33 @@ +/* +* (C) 2015 Jack Lloyd +* (C) 2015 Simon Warta (Kullo GmbH) +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_UTIL_FILESYSTEM_H_ +#define BOTAN_UTIL_FILESYSTEM_H_ + +#include <botan/types.h> +#include <vector> +#include <string> + +namespace Botan { + +/** +* No_Filesystem_Access Exception +*/ +class BOTAN_PUBLIC_API(2,0) No_Filesystem_Access final : public Exception + { + public: + No_Filesystem_Access() : Exception("No filesystem access enabled.") + {} + }; + +BOTAN_TEST_API bool has_filesystem_impl(); + +BOTAN_TEST_API std::vector<std::string> get_files_recursive(const std::string& dir); + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/ghash/ghash.cpp b/comm/third_party/botan/src/lib/utils/ghash/ghash.cpp new file mode 100644 index 0000000000..e24f5e02ca --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/ghash/ghash.cpp @@ -0,0 +1,236 @@ +/* +* GCM GHASH +* (C) 2013,2015,2017 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/ghash.h> +#include <botan/internal/ct_utils.h> +#include <botan/loadstor.h> +#include <botan/cpuid.h> +#include <botan/exceptn.h> + +namespace Botan { + +std::string GHASH::provider() const + { +#if defined(BOTAN_HAS_GHASH_CLMUL_CPU) + if(CPUID::has_carryless_multiply()) + return "clmul"; +#endif + +#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM) + if(CPUID::has_vperm()) + return "vperm"; +#endif + + return "base"; + } + +void GHASH::ghash_multiply(secure_vector<uint8_t>& x, + const uint8_t input[], + size_t blocks) + { +#if defined(BOTAN_HAS_GHASH_CLMUL_CPU) + if(CPUID::has_carryless_multiply()) + { + return ghash_multiply_cpu(x.data(), m_H_pow.data(), input, blocks); + } +#endif + +#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM) + if(CPUID::has_vperm()) + { + return ghash_multiply_vperm(x.data(), m_HM.data(), input, blocks); + } +#endif + + CT::poison(x.data(), x.size()); + + const uint64_t ALL_BITS = 0xFFFFFFFFFFFFFFFF; + + uint64_t X[2] = { + load_be<uint64_t>(x.data(), 0), + load_be<uint64_t>(x.data(), 1) + }; + + for(size_t b = 0; b != blocks; ++b) + { + X[0] ^= load_be<uint64_t>(input, 2*b); + X[1] ^= load_be<uint64_t>(input, 2*b+1); + + uint64_t Z[2] = { 0, 0 }; + + for(size_t i = 0; i != 64; ++i) + { + const uint64_t X0MASK = (ALL_BITS + (X[0] >> 63)) ^ ALL_BITS; + const uint64_t X1MASK = (ALL_BITS + (X[1] >> 63)) ^ ALL_BITS; + + X[0] <<= 1; + X[1] <<= 1; + + Z[0] ^= m_HM[4*i ] & X0MASK; + Z[1] ^= m_HM[4*i+1] & X0MASK; + Z[0] ^= m_HM[4*i+2] & X1MASK; + Z[1] ^= m_HM[4*i+3] & X1MASK; + } + + X[0] = Z[0]; + X[1] = Z[1]; + } + + store_be<uint64_t>(x.data(), X[0], X[1]); + CT::unpoison(x.data(), x.size()); + } + +void GHASH::ghash_update(secure_vector<uint8_t>& ghash, + const uint8_t input[], size_t length) + { + verify_key_set(!m_HM.empty()); + + /* + This assumes if less than block size input then we're just on the + final block and should pad with zeros + */ + + const size_t full_blocks = length / GCM_BS; + const size_t final_bytes = length - (full_blocks * GCM_BS); + + if(full_blocks > 0) + { + ghash_multiply(ghash, input, full_blocks); + } + + if(final_bytes) + { + uint8_t last_block[GCM_BS] = { 0 }; + copy_mem(last_block, input + full_blocks * GCM_BS, final_bytes); + ghash_multiply(ghash, last_block, 1); + secure_scrub_memory(last_block, final_bytes); + } + } + +void GHASH::key_schedule(const uint8_t key[], size_t length) + { + m_H.assign(key, key+length); + m_H_ad.resize(GCM_BS); + m_ad_len = 0; + m_text_len = 0; + + uint64_t H0 = load_be<uint64_t>(m_H.data(), 0); + uint64_t H1 = load_be<uint64_t>(m_H.data(), 1); + + const uint64_t R = 0xE100000000000000; + + m_HM.resize(256); + + // precompute the multiples of H + for(size_t i = 0; i != 2; ++i) + { + for(size_t j = 0; j != 64; ++j) + { + /* + we interleave H^1, H^65, H^2, H^66, H3, H67, H4, H68 + to make indexing nicer in the multiplication code + */ + m_HM[4*j+2*i] = H0; + m_HM[4*j+2*i+1] = H1; + + // GCM's bit ops are reversed so we carry out of the bottom + const uint64_t carry = R * (H1 & 1); + H1 = (H1 >> 1) | (H0 << 63); + H0 = (H0 >> 1) ^ carry; + } + } + +#if defined(BOTAN_HAS_GHASH_CLMUL_CPU) + if(CPUID::has_carryless_multiply()) + { + m_H_pow.resize(8); + ghash_precompute_cpu(m_H.data(), m_H_pow.data()); + } +#endif + } + +void GHASH::start(const uint8_t nonce[], size_t len) + { + BOTAN_ARG_CHECK(len == 16, "GHASH requires a 128-bit nonce"); + m_nonce.assign(nonce, nonce + len); + m_ghash = m_H_ad; + } + +void GHASH::set_associated_data(const uint8_t input[], size_t length) + { + if(m_ghash.empty() == false) + throw Invalid_State("Too late to set AD in GHASH"); + + zeroise(m_H_ad); + + ghash_update(m_H_ad, input, length); + m_ad_len = length; + } + +void GHASH::update_associated_data(const uint8_t ad[], size_t length) + { + verify_key_set(m_ghash.size() == GCM_BS); + m_ad_len += length; + ghash_update(m_ghash, ad, length); + } + +void GHASH::update(const uint8_t input[], size_t length) + { + verify_key_set(m_ghash.size() == GCM_BS); + m_text_len += length; + ghash_update(m_ghash, input, length); + } + +void GHASH::add_final_block(secure_vector<uint8_t>& hash, + size_t ad_len, size_t text_len) + { + /* + * stack buffer is fine here since the text len is public + * and the length of the AD is probably not sensitive either. + */ + uint8_t final_block[GCM_BS]; + store_be<uint64_t>(final_block, 8*ad_len, 8*text_len); + ghash_update(hash, final_block, GCM_BS); + } + +void GHASH::final(uint8_t mac[], size_t mac_len) + { + BOTAN_ARG_CHECK(mac_len > 0 && mac_len <= 16, "GHASH output length"); + add_final_block(m_ghash, m_ad_len, m_text_len); + + for(size_t i = 0; i != mac_len; ++i) + mac[i] = m_ghash[i] ^ m_nonce[i]; + + m_ghash.clear(); + m_text_len = 0; + } + +void GHASH::nonce_hash(secure_vector<uint8_t>& y0, const uint8_t nonce[], size_t nonce_len) + { + BOTAN_ASSERT(m_ghash.size() == 0, "nonce_hash called during wrong time"); + + ghash_update(y0, nonce, nonce_len); + add_final_block(y0, 0, nonce_len); + } + +void GHASH::clear() + { + zap(m_H); + zap(m_HM); + reset(); + } + +void GHASH::reset() + { + zeroise(m_H_ad); + m_ghash.clear(); + m_nonce.clear(); + m_text_len = m_ad_len = 0; + } + +} diff --git a/comm/third_party/botan/src/lib/utils/ghash/ghash.h b/comm/third_party/botan/src/lib/utils/ghash/ghash.h new file mode 100644 index 0000000000..062a04b84b --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/ghash/ghash.h @@ -0,0 +1,110 @@ +/* +* (C) 2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_GCM_GHASH_H_ +#define BOTAN_GCM_GHASH_H_ + +#include <botan/sym_algo.h> + +BOTAN_FUTURE_INTERNAL_HEADER(ghash.h) + +namespace Botan { + +/** +* GCM's GHASH +* This is not intended for general use, but is exposed to allow +* shared code between GCM and GMAC +*/ +class BOTAN_PUBLIC_API(2,0) GHASH final : public SymmetricAlgorithm + { + public: + void set_associated_data(const uint8_t ad[], size_t ad_len); + + secure_vector<uint8_t> BOTAN_DEPRECATED("Use other impl") + nonce_hash(const uint8_t nonce[], size_t nonce_len) + { + secure_vector<uint8_t> y0(GCM_BS); + nonce_hash(y0, nonce, nonce_len); + return y0; + } + + void nonce_hash(secure_vector<uint8_t>& y0, const uint8_t nonce[], size_t len); + + void start(const uint8_t nonce[], size_t len); + + /* + * Assumes input len is multiple of 16 + */ + void update(const uint8_t in[], size_t len); + + /* + * Incremental update of associated data + */ + void update_associated_data(const uint8_t ad[], size_t len); + + secure_vector<uint8_t> BOTAN_DEPRECATED("Use version taking output params") final() + { + secure_vector<uint8_t> mac(GCM_BS); + final(mac.data(), mac.size()); + return mac; + } + + void final(uint8_t out[], size_t out_len); + + Key_Length_Specification key_spec() const override + { return Key_Length_Specification(16); } + + void clear() override; + + void reset(); + + std::string name() const override { return "GHASH"; } + + std::string provider() const; + + void ghash_update(secure_vector<uint8_t>& x, + const uint8_t input[], size_t input_len); + + void add_final_block(secure_vector<uint8_t>& x, + size_t ad_len, size_t pt_len); + private: + +#if defined(BOTAN_HAS_GHASH_CLMUL_CPU) + static void ghash_precompute_cpu(const uint8_t H[16], uint64_t H_pow[4*2]); + + static void ghash_multiply_cpu(uint8_t x[16], + const uint64_t H_pow[4*2], + const uint8_t input[], size_t blocks); +#endif + +#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM) + static void ghash_multiply_vperm(uint8_t x[16], + const uint64_t HM[256], + const uint8_t input[], size_t blocks); +#endif + + void key_schedule(const uint8_t key[], size_t key_len) override; + + void ghash_multiply(secure_vector<uint8_t>& x, + const uint8_t input[], + size_t blocks); + + static const size_t GCM_BS = 16; + + secure_vector<uint8_t> m_H; + secure_vector<uint8_t> m_H_ad; + secure_vector<uint8_t> m_ghash; + secure_vector<uint8_t> m_nonce; + secure_vector<uint64_t> m_HM; + secure_vector<uint64_t> m_H_pow; + size_t m_ad_len = 0; + size_t m_text_len = 0; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp b/comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp new file mode 100644 index 0000000000..5d725933d9 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp @@ -0,0 +1,207 @@ +/* +* Hook for CLMUL/PMULL/VPMSUM +* (C) 2013,2017,2019,2020 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/ghash.h> +#include <botan/internal/simd_32.h> + +#if defined(BOTAN_SIMD_USE_SSE2) + #include <immintrin.h> + #include <wmmintrin.h> +#endif + +namespace Botan { + +namespace { + +BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) reverse_vector(const SIMD_4x32& in) + { +#if defined(BOTAN_SIMD_USE_SSE2) + const __m128i BSWAP_MASK = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + return SIMD_4x32(_mm_shuffle_epi8(in.raw(), BSWAP_MASK)); +#elif defined(BOTAN_SIMD_USE_NEON) + const uint8_t maskb[16] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + const uint8x16_t mask = vld1q_u8(maskb); + return SIMD_4x32(vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(in.raw()), mask))); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const __vector unsigned char mask = {15,14,13,12, 11,10,9,8, 7,6,5,4, 3,2,1,0}; + return SIMD_4x32(vec_perm(in.raw(), in.raw(), mask)); +#endif + } + +template<int M> +BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_CLMUL_ISA) clmul(const SIMD_4x32& H, const SIMD_4x32& x) + { + static_assert(M == 0x00 || M == 0x01 || M == 0x10 || M == 0x11, "Valid clmul mode"); + +#if defined(BOTAN_SIMD_USE_SSE2) + return SIMD_4x32(_mm_clmulepi64_si128(x.raw(), H.raw(), M)); +#elif defined(BOTAN_SIMD_USE_NEON) + const uint64_t a = vgetq_lane_u64(vreinterpretq_u64_u32(x.raw()), M & 0x01); + const uint64_t b = vgetq_lane_u64(vreinterpretq_u64_u32(H.raw()), (M & 0x10) >> 4); + return SIMD_4x32(reinterpret_cast<uint32x4_t>(vmull_p64(a, b))); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const SIMD_4x32 mask_lo = SIMD_4x32(0, 0, 0xFFFFFFFF, 0xFFFFFFFF); + + SIMD_4x32 i1 = x; + SIMD_4x32 i2 = H; + + if(M == 0x11) + { + i1 &= mask_lo; + i2 &= mask_lo; + } + else if(M == 0x10) + { + i1 = i1.shift_elems_left<2>(); + } + else if(M == 0x01) + { + i2 = i2.shift_elems_left<2>(); + } + else if(M == 0x00) + { + i1 = mask_lo.andc(i1); + i2 = mask_lo.andc(i2); + } + + auto i1v = reinterpret_cast<__vector unsigned long long>(i1.raw()); + auto i2v = reinterpret_cast<__vector unsigned long long>(i2.raw()); + +#if defined(__clang__) + auto rv = __builtin_altivec_crypto_vpmsumd(i1v, i2v); +#else + auto rv = __builtin_crypto_vpmsumd(i1v, i2v); +#endif + + return SIMD_4x32(reinterpret_cast<__vector unsigned int>(rv)); +#endif + } + +inline SIMD_4x32 gcm_reduce(const SIMD_4x32& B0, const SIMD_4x32& B1) + { + SIMD_4x32 X0 = B1.shr<31>(); + SIMD_4x32 X1 = B1.shl<1>(); + SIMD_4x32 X2 = B0.shr<31>(); + SIMD_4x32 X3 = B0.shl<1>(); + + X3 |= X0.shift_elems_right<3>(); + X3 |= X2.shift_elems_left<1>(); + X1 |= X0.shift_elems_left<1>(); + + X0 = X1.shl<31>() ^ X1.shl<30>() ^ X1.shl<25>(); + + X1 ^= X0.shift_elems_left<3>(); + + X0 = X1 ^ X3 ^ X0.shift_elems_right<1>(); + X0 ^= X1.shr<7>() ^ X1.shr<2>() ^ X1.shr<1>(); + return X0; + } + +inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_CLMUL_ISA) gcm_multiply(const SIMD_4x32& H, const SIMD_4x32& x) + { + SIMD_4x32 T0 = clmul<0x11>(H, x); + SIMD_4x32 T1 = clmul<0x10>(H, x); + SIMD_4x32 T2 = clmul<0x01>(H, x); + SIMD_4x32 T3 = clmul<0x00>(H, x); + + T1 ^= T2; + T0 ^= T1.shift_elems_right<2>(); + T3 ^= T1.shift_elems_left<2>(); + + return gcm_reduce(T0, T3); + } + +inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_CLMUL_ISA) + gcm_multiply_x4(const SIMD_4x32& H1, const SIMD_4x32& H2, const SIMD_4x32& H3, const SIMD_4x32& H4, + const SIMD_4x32& X1, const SIMD_4x32& X2, const SIMD_4x32& X3, const SIMD_4x32& X4) + { + /* + * Mutiply with delayed reduction, algorithm by Krzysztof Jankowski + * and Pierre Laurent of Intel + */ + + const SIMD_4x32 lo = (clmul<0x00>(H1, X1) ^ clmul<0x00>(H2, X2)) ^ + (clmul<0x00>(H3, X3) ^ clmul<0x00>(H4, X4)); + + const SIMD_4x32 hi = (clmul<0x11>(H1, X1) ^ clmul<0x11>(H2, X2)) ^ + (clmul<0x11>(H3, X3) ^ clmul<0x11>(H4, X4)); + + SIMD_4x32 T; + + T ^= clmul<0x00>(H1 ^ H1.shift_elems_right<2>(), X1 ^ X1.shift_elems_right<2>()); + T ^= clmul<0x00>(H2 ^ H2.shift_elems_right<2>(), X2 ^ X2.shift_elems_right<2>()); + T ^= clmul<0x00>(H3 ^ H3.shift_elems_right<2>(), X3 ^ X3.shift_elems_right<2>()); + T ^= clmul<0x00>(H4 ^ H4.shift_elems_right<2>(), X4 ^ X4.shift_elems_right<2>()); + T ^= lo; + T ^= hi; + + return gcm_reduce(hi ^ T.shift_elems_right<2>(), + lo ^ T.shift_elems_left<2>()); + } + +} + +BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) +void GHASH::ghash_precompute_cpu(const uint8_t H_bytes[16], uint64_t H_pow[4*2]) + { + const SIMD_4x32 H1 = reverse_vector(SIMD_4x32::load_le(H_bytes)); + const SIMD_4x32 H2 = gcm_multiply(H1, H1); + const SIMD_4x32 H3 = gcm_multiply(H1, H2); + const SIMD_4x32 H4 = gcm_multiply(H2, H2); + + H1.store_le(H_pow); + H2.store_le(H_pow + 2); + H3.store_le(H_pow + 4); + H4.store_le(H_pow + 6); + } + +BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) +void GHASH::ghash_multiply_cpu(uint8_t x[16], + const uint64_t H_pow[8], + const uint8_t input[], size_t blocks) + { + /* + * Algorithms 1 and 5 from Intel's CLMUL guide + */ + const SIMD_4x32 H1 = SIMD_4x32::load_le(H_pow); + + SIMD_4x32 a = reverse_vector(SIMD_4x32::load_le(x)); + + if(blocks >= 4) + { + const SIMD_4x32 H2 = SIMD_4x32::load_le(H_pow + 2); + const SIMD_4x32 H3 = SIMD_4x32::load_le(H_pow + 4); + const SIMD_4x32 H4 = SIMD_4x32::load_le(H_pow + 6); + + while(blocks >= 4) + { + const SIMD_4x32 m0 = reverse_vector(SIMD_4x32::load_le(input )); + const SIMD_4x32 m1 = reverse_vector(SIMD_4x32::load_le(input + 16*1)); + const SIMD_4x32 m2 = reverse_vector(SIMD_4x32::load_le(input + 16*2)); + const SIMD_4x32 m3 = reverse_vector(SIMD_4x32::load_le(input + 16*3)); + + a ^= m0; + a = gcm_multiply_x4(H1, H2, H3, H4, m3, m2, m1, a); + + input += 4*16; + blocks -= 4; + } + } + + for(size_t i = 0; i != blocks; ++i) + { + const SIMD_4x32 m = reverse_vector(SIMD_4x32::load_le(input + 16*i)); + + a ^= m; + a = gcm_multiply(H1, a); + } + + a = reverse_vector(a); + a.store_le(x); + } + +} diff --git a/comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/info.txt b/comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/info.txt new file mode 100644 index 0000000000..1f56377a29 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/info.txt @@ -0,0 +1,34 @@ +<defines> +GHASH_CLMUL_CPU -> 20201002 +</defines> + +endian little + +<requires> +simd +</requires> + +<isa> +x86_32:sse2 +x86_32:ssse3 +x86_32:aesni +x86_64:sse2 +x86_64:ssse3 +x86_64:aesni +arm64:neon +arm64:armv8crypto +ppc64:powercrypto +</isa> + +<arch> +x86_32 +x86_64 +arm64 +ppc64 +</arch> + +<cc> +gcc:4.9 +clang:3.8 +msvc +</cc> diff --git a/comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp b/comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp new file mode 100644 index 0000000000..f6f342cb9e --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp @@ -0,0 +1,62 @@ +/* +* (C) 2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/ghash.h> +#include <immintrin.h> + +namespace Botan { + +// TODO: extend this to support NEON and AltiVec + +BOTAN_FUNC_ISA("ssse3") +void GHASH::ghash_multiply_vperm(uint8_t x[16], + const uint64_t HM[256], + const uint8_t input_bytes[], size_t blocks) + { + const __m128i BSWAP_MASK = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + + const __m128i* HM_mm = reinterpret_cast<const __m128i*>(HM); + + __m128i X = _mm_loadu_si128(reinterpret_cast<__m128i*>(x)); + X = _mm_shuffle_epi8(X, BSWAP_MASK); + + const __m128i ones = _mm_set1_epi8(-1); + + for(size_t b = 0; b != blocks; ++b) + { + __m128i M = _mm_loadu_si128(reinterpret_cast<const __m128i*>(input_bytes) + b); + M = _mm_shuffle_epi8(M, BSWAP_MASK); + + X = _mm_xor_si128(X, M); + + __m128i Z = _mm_setzero_si128(); + + for(size_t i = 0; i != 64; i += 2) + { + const __m128i HM0 = _mm_loadu_si128(HM_mm + 2*i); + const __m128i HM1 = _mm_loadu_si128(HM_mm + 2*i + 1); + const __m128i HM2 = _mm_loadu_si128(HM_mm + 2*i + 2); + const __m128i HM3 = _mm_loadu_si128(HM_mm + 2*i + 3); + + const __m128i XMASK1 = _mm_add_epi64(_mm_srli_epi64(X, 63), ones); + X = _mm_slli_epi64(X, 1); + const __m128i XMASK2 = _mm_add_epi64(_mm_srli_epi64(X, 63), ones); + X = _mm_slli_epi64(X, 1); + + Z = _mm_xor_si128(Z, _mm_andnot_si128(_mm_unpackhi_epi64(XMASK1, XMASK1), HM0)); + Z = _mm_xor_si128(Z, _mm_andnot_si128(_mm_unpacklo_epi64(XMASK1, XMASK1), HM1)); + Z = _mm_xor_si128(Z, _mm_andnot_si128(_mm_unpackhi_epi64(XMASK2, XMASK2), HM2)); + Z = _mm_xor_si128(Z, _mm_andnot_si128(_mm_unpacklo_epi64(XMASK2, XMASK2), HM3)); + } + + X = _mm_shuffle_epi32(Z, _MM_SHUFFLE(1, 0, 3, 2)); + } + + X = _mm_shuffle_epi8(X, BSWAP_MASK); + _mm_storeu_si128(reinterpret_cast<__m128i*>(x), X); + } + +} diff --git a/comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/info.txt b/comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/info.txt new file mode 100644 index 0000000000..41d5e1e1b1 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/info.txt @@ -0,0 +1,8 @@ +<defines> +GHASH_CLMUL_VPERM -> 20201002 +</defines> + +<isa> +sse2 +ssse3 +</isa> diff --git a/comm/third_party/botan/src/lib/utils/ghash/info.txt b/comm/third_party/botan/src/lib/utils/ghash/info.txt new file mode 100644 index 0000000000..4e519eb3b5 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/ghash/info.txt @@ -0,0 +1,3 @@ +<defines> +GHASH -> 20201002 +</defines> diff --git a/comm/third_party/botan/src/lib/utils/http_util/http_util.cpp b/comm/third_party/botan/src/lib/utils/http_util/http_util.cpp new file mode 100644 index 0000000000..342fe2e843 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/http_util/http_util.cpp @@ -0,0 +1,267 @@ +/* +* Sketchy HTTP client +* (C) 2013,2016 Jack Lloyd +* 2017 RenĂ© Korthaus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/http_util.h> +#include <botan/parsing.h> +#include <botan/hex.h> +#include <botan/internal/os_utils.h> +#include <botan/internal/socket.h> +#include <botan/internal/stl_util.h> +#include <sstream> + +namespace Botan { + +namespace HTTP { + +namespace { + +/* +* Connect to a host, write some bytes, then read until the server +* closes the socket. +*/ +std::string http_transact(const std::string& hostname, + const std::string& service, + const std::string& message, + std::chrono::milliseconds timeout) + { + std::unique_ptr<OS::Socket> socket; + + const std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now(); + + try + { + socket = OS::open_socket(hostname, service, timeout); + if(!socket) + throw Not_Implemented("No socket support enabled in build"); + } + catch(std::exception& e) + { + throw HTTP_Error("HTTP connection to " + hostname + " failed: " + e.what()); + } + + // Blocks until entire message has been written + socket->write(cast_char_ptr_to_uint8(message.data()), + message.size()); + + if(std::chrono::system_clock::now() - start_time > timeout) + throw HTTP_Error("Timeout during writing message body"); + + std::ostringstream oss; + std::vector<uint8_t> buf(BOTAN_DEFAULT_BUFFER_SIZE); + while(true) + { + const size_t got = socket->read(buf.data(), buf.size()); + if(got == 0) // EOF + break; + + if(std::chrono::system_clock::now() - start_time > timeout) + throw HTTP_Error("Timeout while reading message body"); + + oss.write(cast_uint8_ptr_to_char(buf.data()), + static_cast<std::streamsize>(got)); + } + + return oss.str(); + } + +} + +std::string url_encode(const std::string& in) + { + std::ostringstream out; + + for(auto c : in) + { + if(c >= 'A' && c <= 'Z') + out << c; + else if(c >= 'a' && c <= 'z') + out << c; + else if(c >= '0' && c <= '9') + out << c; + else if(c == '-' || c == '_' || c == '.' || c == '~') + out << c; + else + out << '%' << hex_encode(cast_char_ptr_to_uint8(&c), 1); + } + + return out.str(); + } + +std::ostream& operator<<(std::ostream& o, const Response& resp) + { + o << "HTTP " << resp.status_code() << " " << resp.status_message() << "\n"; + for(auto h : resp.headers()) + o << "Header '" << h.first << "' = '" << h.second << "'\n"; + o << "Body " << std::to_string(resp.body().size()) << " bytes:\n"; + o.write(cast_uint8_ptr_to_char(resp.body().data()), resp.body().size()); + return o; + } + +Response http_sync(http_exch_fn http_transact, + const std::string& verb, + const std::string& url, + const std::string& content_type, + const std::vector<uint8_t>& body, + size_t allowable_redirects) + { + if(url.empty()) + throw HTTP_Error("URL empty"); + + const auto protocol_host_sep = url.find("://"); + if(protocol_host_sep == std::string::npos) + throw HTTP_Error("Invalid URL '" + url + "'"); + + const auto host_loc_sep = url.find('/', protocol_host_sep + 3); + + std::string hostname, loc, service; + + if(host_loc_sep == std::string::npos) + { + hostname = url.substr(protocol_host_sep + 3, std::string::npos); + loc = "/"; + } + else + { + hostname = url.substr(protocol_host_sep + 3, host_loc_sep-protocol_host_sep-3); + loc = url.substr(host_loc_sep, std::string::npos); + } + + const auto port_sep = hostname.find(":"); + if(port_sep == std::string::npos) + { + service = "http"; + // hostname not modified + } + else + { + service = hostname.substr(port_sep + 1, std::string::npos); + hostname = hostname.substr(0, port_sep); + } + + std::ostringstream outbuf; + + outbuf << verb << " " << loc << " HTTP/1.0\r\n"; + outbuf << "Host: " << hostname << "\r\n"; + + if(verb == "GET") + { + outbuf << "Accept: */*\r\n"; + outbuf << "Cache-Control: no-cache\r\n"; + } + else if(verb == "POST") + outbuf << "Content-Length: " << body.size() << "\r\n"; + + if(!content_type.empty()) + outbuf << "Content-Type: " << content_type << "\r\n"; + outbuf << "Connection: close\r\n\r\n"; + outbuf.write(cast_uint8_ptr_to_char(body.data()), body.size()); + + std::istringstream io(http_transact(hostname, service, outbuf.str())); + + std::string line1; + std::getline(io, line1); + if(!io || line1.empty()) + throw HTTP_Error("No response"); + + std::stringstream response_stream(line1); + std::string http_version; + unsigned int status_code; + std::string status_message; + + response_stream >> http_version >> status_code; + + std::getline(response_stream, status_message); + + if(!response_stream || http_version.substr(0,5) != "HTTP/") + throw HTTP_Error("Not an HTTP response"); + + std::map<std::string, std::string> headers; + std::string header_line; + while (std::getline(io, header_line) && header_line != "\r") + { + auto sep = header_line.find(": "); + if(sep == std::string::npos || sep > header_line.size() - 2) + throw HTTP_Error("Invalid HTTP header " + header_line); + const std::string key = header_line.substr(0, sep); + + if(sep + 2 < header_line.size() - 1) + { + const std::string val = header_line.substr(sep + 2, (header_line.size() - 1) - (sep + 2)); + headers[key] = val; + } + } + + if(status_code == 301 && headers.count("Location")) + { + if(allowable_redirects == 0) + throw HTTP_Error("HTTP redirection count exceeded"); + return GET_sync(headers["Location"], allowable_redirects - 1); + } + + std::vector<uint8_t> resp_body; + std::vector<uint8_t> buf(4096); + while(io.good()) + { + io.read(cast_uint8_ptr_to_char(buf.data()), buf.size()); + const size_t got = static_cast<size_t>(io.gcount()); + resp_body.insert(resp_body.end(), buf.data(), &buf[got]); + } + + const std::string header_size = search_map(headers, std::string("Content-Length")); + + if(!header_size.empty()) + { + if(resp_body.size() != to_u32bit(header_size)) + throw HTTP_Error("Content-Length disagreement, header says " + + header_size + " got " + std::to_string(resp_body.size())); + } + + return Response(status_code, status_message, resp_body, headers); + } + +Response http_sync(const std::string& verb, + const std::string& url, + const std::string& content_type, + const std::vector<uint8_t>& body, + size_t allowable_redirects, + std::chrono::milliseconds timeout) + { + auto transact_with_timeout = + [timeout](const std::string& hostname, const std::string& service, const std::string& message) + { + return http_transact(hostname, service, message, timeout); + }; + + return http_sync( + transact_with_timeout, + verb, + url, + content_type, + body, + allowable_redirects); + } + +Response GET_sync(const std::string& url, + size_t allowable_redirects, + std::chrono::milliseconds timeout) + { + return http_sync("GET", url, "", std::vector<uint8_t>(), allowable_redirects, timeout); + } + +Response POST_sync(const std::string& url, + const std::string& content_type, + const std::vector<uint8_t>& body, + size_t allowable_redirects, + std::chrono::milliseconds timeout) + { + return http_sync("POST", url, content_type, body, allowable_redirects, timeout); + } + +} + +} diff --git a/comm/third_party/botan/src/lib/utils/http_util/http_util.h b/comm/third_party/botan/src/lib/utils/http_util/http_util.h new file mode 100644 index 0000000000..7ad7c5828c --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/http_util/http_util.h @@ -0,0 +1,107 @@ +/* +* HTTP utilities +* (C) 2013 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_UTILS_URLGET_H_ +#define BOTAN_UTILS_URLGET_H_ + +#include <botan/types.h> +#include <botan/exceptn.h> +#include <vector> +#include <map> +#include <string> +#include <functional> +#include <chrono> + +BOTAN_FUTURE_INTERNAL_HEADER(http_util.h) + +namespace Botan { + +namespace HTTP { + +/** +* HTTP_Error Exception +*/ +class BOTAN_PUBLIC_API(2,0) HTTP_Error final : public Exception + { + public: + explicit HTTP_Error(const std::string& msg) : + Exception("HTTP error " + msg) + {} + + ErrorType error_type() const noexcept override { return ErrorType::HttpError; } + + }; + +class Response final + { + public: + Response() : m_status_code(0), m_status_message("Uninitialized") {} + + Response(unsigned int status_code, const std::string& status_message, + const std::vector<uint8_t>& body, + const std::map<std::string, std::string>& headers) : + m_status_code(status_code), + m_status_message(status_message), + m_body(body), + m_headers(headers) {} + + unsigned int status_code() const { return m_status_code; } + + const std::vector<uint8_t>& body() const { return m_body; } + + const std::map<std::string, std::string>& headers() const { return m_headers; } + + std::string status_message() const { return m_status_message; } + + void throw_unless_ok() + { + if(status_code() != 200) + throw HTTP_Error(status_message()); + } + + private: + unsigned int m_status_code; + std::string m_status_message; + std::vector<uint8_t> m_body; + std::map<std::string, std::string> m_headers; + }; + +BOTAN_PUBLIC_API(2,0) std::ostream& operator<<(std::ostream& o, const Response& resp); + +typedef std::function<std::string (const std::string&, const std::string&, const std::string&)> http_exch_fn; + +BOTAN_PUBLIC_API(2,0) Response http_sync(http_exch_fn fn, + const std::string& verb, + const std::string& url, + const std::string& content_type, + const std::vector<uint8_t>& body, + size_t allowable_redirects); + +BOTAN_PUBLIC_API(2,0) Response http_sync(const std::string& verb, + const std::string& url, + const std::string& content_type, + const std::vector<uint8_t>& body, + size_t allowable_redirects, + std::chrono::milliseconds timeout = std::chrono::milliseconds(3000)); + +BOTAN_PUBLIC_API(2,0) Response GET_sync(const std::string& url, + size_t allowable_redirects = 1, + std::chrono::milliseconds timeout = std::chrono::milliseconds(3000)); + +BOTAN_PUBLIC_API(2,0) Response POST_sync(const std::string& url, + const std::string& content_type, + const std::vector<uint8_t>& body, + size_t allowable_redirects = 1, + std::chrono::milliseconds timeout = std::chrono::milliseconds(3000)); + +BOTAN_PUBLIC_API(2,0) std::string url_encode(const std::string& url); + +} + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/http_util/info.txt b/comm/third_party/botan/src/lib/utils/http_util/info.txt new file mode 100644 index 0000000000..a3ebc249e2 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/http_util/info.txt @@ -0,0 +1,11 @@ +<defines> +HTTP_UTIL -> 20171003 +</defines> + +<header:public> +http_util.h +</header:public> + +<requires> +socket +</requires> diff --git a/comm/third_party/botan/src/lib/utils/info.txt b/comm/third_party/botan/src/lib/utils/info.txt new file mode 100644 index 0000000000..93ae795ef5 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/info.txt @@ -0,0 +1,43 @@ +<defines> +UTIL_FUNCTIONS -> 20180903 +</defines> + +load_on always + +<header:public> +assert.h +bswap.h +calendar.h +charset.h +compiler.h +data_src.h +database.h +exceptn.h +loadstor.h +mem_ops.h +mul128.h +mutex.h +parsing.h +rotate.h +types.h +version.h +stl_compatibility.h +</header:public> + +<header:internal> +bit_ops.h +codec_base.h +ct_utils.h +donna128.h +filesystem.h +os_utils.h +prefetch.h +rounding.h +safeint.h +stl_util.h +timer.h +</header:internal> + +<requires> +cpuid +</requires> diff --git a/comm/third_party/botan/src/lib/utils/loadstor.h b/comm/third_party/botan/src/lib/utils/loadstor.h new file mode 100644 index 0000000000..9a8e9d4bea --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/loadstor.h @@ -0,0 +1,701 @@ +/* +* Load/Store Operators +* (C) 1999-2007,2015,2017 Jack Lloyd +* 2007 Yves Jerschow +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_LOAD_STORE_H_ +#define BOTAN_LOAD_STORE_H_ + +#include <botan/types.h> +#include <botan/bswap.h> +#include <botan/mem_ops.h> +#include <vector> + +BOTAN_FUTURE_INTERNAL_HEADER(loadstor.h) + +#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) + #define BOTAN_ENDIAN_N2L(x) reverse_bytes(x) + #define BOTAN_ENDIAN_L2N(x) reverse_bytes(x) + #define BOTAN_ENDIAN_N2B(x) (x) + #define BOTAN_ENDIAN_B2N(x) (x) + +#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) + #define BOTAN_ENDIAN_N2L(x) (x) + #define BOTAN_ENDIAN_L2N(x) (x) + #define BOTAN_ENDIAN_N2B(x) reverse_bytes(x) + #define BOTAN_ENDIAN_B2N(x) reverse_bytes(x) + +#endif + +namespace Botan { + +/** +* Byte extraction +* @param byte_num which byte to extract, 0 == highest byte +* @param input the value to extract from +* @return byte byte_num of input +*/ +template<typename T> inline constexpr uint8_t get_byte(size_t byte_num, T input) + { + return static_cast<uint8_t>( + input >> (((~byte_num)&(sizeof(T)-1)) << 3) + ); + } + +/** +* Make a uint16_t from two bytes +* @param i0 the first byte +* @param i1 the second byte +* @return i0 || i1 +*/ +inline constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1) + { + return static_cast<uint16_t>((static_cast<uint16_t>(i0) << 8) | i1); + } + +/** +* Make a uint32_t from four bytes +* @param i0 the first byte +* @param i1 the second byte +* @param i2 the third byte +* @param i3 the fourth byte +* @return i0 || i1 || i2 || i3 +*/ +inline constexpr uint32_t make_uint32(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3) + { + return ((static_cast<uint32_t>(i0) << 24) | + (static_cast<uint32_t>(i1) << 16) | + (static_cast<uint32_t>(i2) << 8) | + (static_cast<uint32_t>(i3))); + } + +/** +* Make a uint64_t from eight bytes +* @param i0 the first byte +* @param i1 the second byte +* @param i2 the third byte +* @param i3 the fourth byte +* @param i4 the fifth byte +* @param i5 the sixth byte +* @param i6 the seventh byte +* @param i7 the eighth byte +* @return i0 || i1 || i2 || i3 || i4 || i5 || i6 || i7 +*/ +inline constexpr uint64_t make_uint64(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3, + uint8_t i4, uint8_t i5, uint8_t i6, uint8_t i7) + { + return ((static_cast<uint64_t>(i0) << 56) | + (static_cast<uint64_t>(i1) << 48) | + (static_cast<uint64_t>(i2) << 40) | + (static_cast<uint64_t>(i3) << 32) | + (static_cast<uint64_t>(i4) << 24) | + (static_cast<uint64_t>(i5) << 16) | + (static_cast<uint64_t>(i6) << 8) | + (static_cast<uint64_t>(i7))); + } + +/** +* Load a big-endian word +* @param in a pointer to some bytes +* @param off an offset into the array +* @return off'th T of in, as a big-endian value +*/ +template<typename T> +inline T load_be(const uint8_t in[], size_t off) + { + in += off * sizeof(T); + T out = 0; + for(size_t i = 0; i != sizeof(T); ++i) + out = static_cast<T>((out << 8) | in[i]); + return out; + } + +/** +* Load a little-endian word +* @param in a pointer to some bytes +* @param off an offset into the array +* @return off'th T of in, as a litte-endian value +*/ +template<typename T> +inline T load_le(const uint8_t in[], size_t off) + { + in += off * sizeof(T); + T out = 0; + for(size_t i = 0; i != sizeof(T); ++i) + out = (out << 8) | in[sizeof(T)-1-i]; + return out; + } + +/** +* Load a big-endian uint16_t +* @param in a pointer to some bytes +* @param off an offset into the array +* @return off'th uint16_t of in, as a big-endian value +*/ +template<> +inline uint16_t load_be<uint16_t>(const uint8_t in[], size_t off) + { + in += off * sizeof(uint16_t); + +#if defined(BOTAN_ENDIAN_N2B) + uint16_t x; + typecast_copy(x, in); + return BOTAN_ENDIAN_N2B(x); +#else + return make_uint16(in[0], in[1]); +#endif + } + +/** +* Load a little-endian uint16_t +* @param in a pointer to some bytes +* @param off an offset into the array +* @return off'th uint16_t of in, as a little-endian value +*/ +template<> +inline uint16_t load_le<uint16_t>(const uint8_t in[], size_t off) + { + in += off * sizeof(uint16_t); + +#if defined(BOTAN_ENDIAN_N2L) + uint16_t x; + typecast_copy(x, in); + return BOTAN_ENDIAN_N2L(x); +#else + return make_uint16(in[1], in[0]); +#endif + } + +/** +* Load a big-endian uint32_t +* @param in a pointer to some bytes +* @param off an offset into the array +* @return off'th uint32_t of in, as a big-endian value +*/ +template<> +inline uint32_t load_be<uint32_t>(const uint8_t in[], size_t off) + { + in += off * sizeof(uint32_t); +#if defined(BOTAN_ENDIAN_N2B) + uint32_t x; + typecast_copy(x, in); + return BOTAN_ENDIAN_N2B(x); +#else + return make_uint32(in[0], in[1], in[2], in[3]); +#endif + } + +/** +* Load a little-endian uint32_t +* @param in a pointer to some bytes +* @param off an offset into the array +* @return off'th uint32_t of in, as a little-endian value +*/ +template<> +inline uint32_t load_le<uint32_t>(const uint8_t in[], size_t off) + { + in += off * sizeof(uint32_t); +#if defined(BOTAN_ENDIAN_N2L) + uint32_t x; + typecast_copy(x, in); + return BOTAN_ENDIAN_N2L(x); +#else + return make_uint32(in[3], in[2], in[1], in[0]); +#endif + } + +/** +* Load a big-endian uint64_t +* @param in a pointer to some bytes +* @param off an offset into the array +* @return off'th uint64_t of in, as a big-endian value +*/ +template<> +inline uint64_t load_be<uint64_t>(const uint8_t in[], size_t off) + { + in += off * sizeof(uint64_t); +#if defined(BOTAN_ENDIAN_N2B) + uint64_t x; + typecast_copy(x, in); + return BOTAN_ENDIAN_N2B(x); +#else + return make_uint64(in[0], in[1], in[2], in[3], + in[4], in[5], in[6], in[7]); +#endif + } + +/** +* Load a little-endian uint64_t +* @param in a pointer to some bytes +* @param off an offset into the array +* @return off'th uint64_t of in, as a little-endian value +*/ +template<> +inline uint64_t load_le<uint64_t>(const uint8_t in[], size_t off) + { + in += off * sizeof(uint64_t); +#if defined(BOTAN_ENDIAN_N2L) + uint64_t x; + typecast_copy(x, in); + return BOTAN_ENDIAN_N2L(x); +#else + return make_uint64(in[7], in[6], in[5], in[4], + in[3], in[2], in[1], in[0]); +#endif + } + +/** +* Load two little-endian words +* @param in a pointer to some bytes +* @param x0 where the first word will be written +* @param x1 where the second word will be written +*/ +template<typename T> +inline void load_le(const uint8_t in[], T& x0, T& x1) + { + x0 = load_le<T>(in, 0); + x1 = load_le<T>(in, 1); + } + +/** +* Load four little-endian words +* @param in a pointer to some bytes +* @param x0 where the first word will be written +* @param x1 where the second word will be written +* @param x2 where the third word will be written +* @param x3 where the fourth word will be written +*/ +template<typename T> +inline void load_le(const uint8_t in[], + T& x0, T& x1, T& x2, T& x3) + { + x0 = load_le<T>(in, 0); + x1 = load_le<T>(in, 1); + x2 = load_le<T>(in, 2); + x3 = load_le<T>(in, 3); + } + +/** +* Load eight little-endian words +* @param in a pointer to some bytes +* @param x0 where the first word will be written +* @param x1 where the second word will be written +* @param x2 where the third word will be written +* @param x3 where the fourth word will be written +* @param x4 where the fifth word will be written +* @param x5 where the sixth word will be written +* @param x6 where the seventh word will be written +* @param x7 where the eighth word will be written +*/ +template<typename T> +inline void load_le(const uint8_t in[], + T& x0, T& x1, T& x2, T& x3, + T& x4, T& x5, T& x6, T& x7) + { + x0 = load_le<T>(in, 0); + x1 = load_le<T>(in, 1); + x2 = load_le<T>(in, 2); + x3 = load_le<T>(in, 3); + x4 = load_le<T>(in, 4); + x5 = load_le<T>(in, 5); + x6 = load_le<T>(in, 6); + x7 = load_le<T>(in, 7); + } + +/** +* Load a variable number of little-endian words +* @param out the output array of words +* @param in the input array of bytes +* @param count how many words are in in +*/ +template<typename T> +inline void load_le(T out[], + const uint8_t in[], + size_t count) + { + if(count > 0) + { +#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) + typecast_copy(out, in, count); + +#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) + typecast_copy(out, in, count); + + const size_t blocks = count - (count % 4); + const size_t left = count - blocks; + + for(size_t i = 0; i != blocks; i += 4) + bswap_4(out + i); + + for(size_t i = 0; i != left; ++i) + out[blocks+i] = reverse_bytes(out[blocks+i]); +#else + for(size_t i = 0; i != count; ++i) + out[i] = load_le<T>(in, i); +#endif + } + } + +/** +* Load two big-endian words +* @param in a pointer to some bytes +* @param x0 where the first word will be written +* @param x1 where the second word will be written +*/ +template<typename T> +inline void load_be(const uint8_t in[], T& x0, T& x1) + { + x0 = load_be<T>(in, 0); + x1 = load_be<T>(in, 1); + } + +/** +* Load four big-endian words +* @param in a pointer to some bytes +* @param x0 where the first word will be written +* @param x1 where the second word will be written +* @param x2 where the third word will be written +* @param x3 where the fourth word will be written +*/ +template<typename T> +inline void load_be(const uint8_t in[], + T& x0, T& x1, T& x2, T& x3) + { + x0 = load_be<T>(in, 0); + x1 = load_be<T>(in, 1); + x2 = load_be<T>(in, 2); + x3 = load_be<T>(in, 3); + } + +/** +* Load eight big-endian words +* @param in a pointer to some bytes +* @param x0 where the first word will be written +* @param x1 where the second word will be written +* @param x2 where the third word will be written +* @param x3 where the fourth word will be written +* @param x4 where the fifth word will be written +* @param x5 where the sixth word will be written +* @param x6 where the seventh word will be written +* @param x7 where the eighth word will be written +*/ +template<typename T> +inline void load_be(const uint8_t in[], + T& x0, T& x1, T& x2, T& x3, + T& x4, T& x5, T& x6, T& x7) + { + x0 = load_be<T>(in, 0); + x1 = load_be<T>(in, 1); + x2 = load_be<T>(in, 2); + x3 = load_be<T>(in, 3); + x4 = load_be<T>(in, 4); + x5 = load_be<T>(in, 5); + x6 = load_be<T>(in, 6); + x7 = load_be<T>(in, 7); + } + +/** +* Load a variable number of big-endian words +* @param out the output array of words +* @param in the input array of bytes +* @param count how many words are in in +*/ +template<typename T> +inline void load_be(T out[], + const uint8_t in[], + size_t count) + { + if(count > 0) + { +#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) + typecast_copy(out, in, count); + +#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) + typecast_copy(out, in, count); + const size_t blocks = count - (count % 4); + const size_t left = count - blocks; + + for(size_t i = 0; i != blocks; i += 4) + bswap_4(out + i); + + for(size_t i = 0; i != left; ++i) + out[blocks+i] = reverse_bytes(out[blocks+i]); +#else + for(size_t i = 0; i != count; ++i) + out[i] = load_be<T>(in, i); +#endif + } + } + +/** +* Store a big-endian uint16_t +* @param in the input uint16_t +* @param out the byte array to write to +*/ +inline void store_be(uint16_t in, uint8_t out[2]) + { +#if defined(BOTAN_ENDIAN_N2B) + uint16_t o = BOTAN_ENDIAN_N2B(in); + typecast_copy(out, o); +#else + out[0] = get_byte(0, in); + out[1] = get_byte(1, in); +#endif + } + +/** +* Store a little-endian uint16_t +* @param in the input uint16_t +* @param out the byte array to write to +*/ +inline void store_le(uint16_t in, uint8_t out[2]) + { +#if defined(BOTAN_ENDIAN_N2L) + uint16_t o = BOTAN_ENDIAN_N2L(in); + typecast_copy(out, o); +#else + out[0] = get_byte(1, in); + out[1] = get_byte(0, in); +#endif + } + +/** +* Store a big-endian uint32_t +* @param in the input uint32_t +* @param out the byte array to write to +*/ +inline void store_be(uint32_t in, uint8_t out[4]) + { +#if defined(BOTAN_ENDIAN_B2N) + uint32_t o = BOTAN_ENDIAN_B2N(in); + typecast_copy(out, o); +#else + out[0] = get_byte(0, in); + out[1] = get_byte(1, in); + out[2] = get_byte(2, in); + out[3] = get_byte(3, in); +#endif + } + +/** +* Store a little-endian uint32_t +* @param in the input uint32_t +* @param out the byte array to write to +*/ +inline void store_le(uint32_t in, uint8_t out[4]) + { +#if defined(BOTAN_ENDIAN_L2N) + uint32_t o = BOTAN_ENDIAN_L2N(in); + typecast_copy(out, o); +#else + out[0] = get_byte(3, in); + out[1] = get_byte(2, in); + out[2] = get_byte(1, in); + out[3] = get_byte(0, in); +#endif + } + +/** +* Store a big-endian uint64_t +* @param in the input uint64_t +* @param out the byte array to write to +*/ +inline void store_be(uint64_t in, uint8_t out[8]) + { +#if defined(BOTAN_ENDIAN_B2N) + uint64_t o = BOTAN_ENDIAN_B2N(in); + typecast_copy(out, o); +#else + out[0] = get_byte(0, in); + out[1] = get_byte(1, in); + out[2] = get_byte(2, in); + out[3] = get_byte(3, in); + out[4] = get_byte(4, in); + out[5] = get_byte(5, in); + out[6] = get_byte(6, in); + out[7] = get_byte(7, in); +#endif + } + +/** +* Store a little-endian uint64_t +* @param in the input uint64_t +* @param out the byte array to write to +*/ +inline void store_le(uint64_t in, uint8_t out[8]) + { +#if defined(BOTAN_ENDIAN_L2N) + uint64_t o = BOTAN_ENDIAN_L2N(in); + typecast_copy(out, o); +#else + out[0] = get_byte(7, in); + out[1] = get_byte(6, in); + out[2] = get_byte(5, in); + out[3] = get_byte(4, in); + out[4] = get_byte(3, in); + out[5] = get_byte(2, in); + out[6] = get_byte(1, in); + out[7] = get_byte(0, in); +#endif + } + +/** +* Store two little-endian words +* @param out the output byte array +* @param x0 the first word +* @param x1 the second word +*/ +template<typename T> +inline void store_le(uint8_t out[], T x0, T x1) + { + store_le(x0, out + (0 * sizeof(T))); + store_le(x1, out + (1 * sizeof(T))); + } + +/** +* Store two big-endian words +* @param out the output byte array +* @param x0 the first word +* @param x1 the second word +*/ +template<typename T> +inline void store_be(uint8_t out[], T x0, T x1) + { + store_be(x0, out + (0 * sizeof(T))); + store_be(x1, out + (1 * sizeof(T))); + } + +/** +* Store four little-endian words +* @param out the output byte array +* @param x0 the first word +* @param x1 the second word +* @param x2 the third word +* @param x3 the fourth word +*/ +template<typename T> +inline void store_le(uint8_t out[], T x0, T x1, T x2, T x3) + { + store_le(x0, out + (0 * sizeof(T))); + store_le(x1, out + (1 * sizeof(T))); + store_le(x2, out + (2 * sizeof(T))); + store_le(x3, out + (3 * sizeof(T))); + } + +/** +* Store four big-endian words +* @param out the output byte array +* @param x0 the first word +* @param x1 the second word +* @param x2 the third word +* @param x3 the fourth word +*/ +template<typename T> +inline void store_be(uint8_t out[], T x0, T x1, T x2, T x3) + { + store_be(x0, out + (0 * sizeof(T))); + store_be(x1, out + (1 * sizeof(T))); + store_be(x2, out + (2 * sizeof(T))); + store_be(x3, out + (3 * sizeof(T))); + } + +/** +* Store eight little-endian words +* @param out the output byte array +* @param x0 the first word +* @param x1 the second word +* @param x2 the third word +* @param x3 the fourth word +* @param x4 the fifth word +* @param x5 the sixth word +* @param x6 the seventh word +* @param x7 the eighth word +*/ +template<typename T> +inline void store_le(uint8_t out[], T x0, T x1, T x2, T x3, + T x4, T x5, T x6, T x7) + { + store_le(x0, out + (0 * sizeof(T))); + store_le(x1, out + (1 * sizeof(T))); + store_le(x2, out + (2 * sizeof(T))); + store_le(x3, out + (3 * sizeof(T))); + store_le(x4, out + (4 * sizeof(T))); + store_le(x5, out + (5 * sizeof(T))); + store_le(x6, out + (6 * sizeof(T))); + store_le(x7, out + (7 * sizeof(T))); + } + +/** +* Store eight big-endian words +* @param out the output byte array +* @param x0 the first word +* @param x1 the second word +* @param x2 the third word +* @param x3 the fourth word +* @param x4 the fifth word +* @param x5 the sixth word +* @param x6 the seventh word +* @param x7 the eighth word +*/ +template<typename T> +inline void store_be(uint8_t out[], T x0, T x1, T x2, T x3, + T x4, T x5, T x6, T x7) + { + store_be(x0, out + (0 * sizeof(T))); + store_be(x1, out + (1 * sizeof(T))); + store_be(x2, out + (2 * sizeof(T))); + store_be(x3, out + (3 * sizeof(T))); + store_be(x4, out + (4 * sizeof(T))); + store_be(x5, out + (5 * sizeof(T))); + store_be(x6, out + (6 * sizeof(T))); + store_be(x7, out + (7 * sizeof(T))); + } + +template<typename T> +void copy_out_be(uint8_t out[], size_t out_bytes, const T in[]) + { + while(out_bytes >= sizeof(T)) + { + store_be(in[0], out); + out += sizeof(T); + out_bytes -= sizeof(T); + in += 1; + } + + for(size_t i = 0; i != out_bytes; ++i) + out[i] = get_byte(i%8, in[0]); + } + +template<typename T, typename Alloc> +void copy_out_vec_be(uint8_t out[], size_t out_bytes, const std::vector<T, Alloc>& in) + { + copy_out_be(out, out_bytes, in.data()); + } + +template<typename T> +void copy_out_le(uint8_t out[], size_t out_bytes, const T in[]) + { + while(out_bytes >= sizeof(T)) + { + store_le(in[0], out); + out += sizeof(T); + out_bytes -= sizeof(T); + in += 1; + } + + for(size_t i = 0; i != out_bytes; ++i) + out[i] = get_byte(sizeof(T) - 1 - (i % 8), in[0]); + } + +template<typename T, typename Alloc> +void copy_out_vec_le(uint8_t out[], size_t out_bytes, const std::vector<T, Alloc>& in) + { + copy_out_le(out, out_bytes, in.data()); + } + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/locking_allocator/info.txt b/comm/third_party/botan/src/lib/utils/locking_allocator/info.txt new file mode 100644 index 0000000000..5d7a2ba83e --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/locking_allocator/info.txt @@ -0,0 +1,12 @@ +<defines> +LOCKING_ALLOCATOR -> 20131128 +</defines> + +<os_features> +posix1 +virtual_lock +</os_features> + +<requires> +mem_pool +</requires> diff --git a/comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.cpp b/comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.cpp new file mode 100644 index 0000000000..1c10c362b6 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.cpp @@ -0,0 +1,75 @@ +/* +* Mlock Allocator +* (C) 2012,2014,2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/locking_allocator.h> +#include <botan/internal/os_utils.h> +#include <botan/internal/mem_pool.h> + +namespace Botan { + +void* mlock_allocator::allocate(size_t num_elems, size_t elem_size) + { + if(!m_pool) + return nullptr; + + const size_t n = num_elems * elem_size; + if(n / elem_size != num_elems) + return nullptr; // overflow! + + return m_pool->allocate(n); + } + +bool mlock_allocator::deallocate(void* p, size_t num_elems, size_t elem_size) noexcept + { + if(!m_pool) + return false; + + size_t n = num_elems * elem_size; + + /* + We return nullptr in allocate if there was an overflow, so if an + overflow occurs here we know the pointer was not allocated by this pool. + */ + if(n / elem_size != num_elems) + return false; + + return m_pool->deallocate(p, n); + } + +mlock_allocator::mlock_allocator() + { + const size_t mem_to_lock = OS::get_memory_locking_limit(); + const size_t page_size = OS::system_page_size(); + + if(mem_to_lock > 0 && mem_to_lock % page_size == 0) + { + m_locked_pages = OS::allocate_locked_pages(mem_to_lock / page_size); + + if(m_locked_pages.size() > 0) + { + m_pool.reset(new Memory_Pool(m_locked_pages, page_size)); + } + } + } + +mlock_allocator::~mlock_allocator() + { + if(m_pool) + { + m_pool.reset(); + // OS::free_locked_pages scrubs the memory before free + OS::free_locked_pages(m_locked_pages); + } + } + +mlock_allocator& mlock_allocator::instance() + { + static mlock_allocator mlock; + return mlock; + } + +} diff --git a/comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.h b/comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.h new file mode 100644 index 0000000000..8d1d249809 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.h @@ -0,0 +1,45 @@ +/* +* Mlock Allocator +* (C) 2012 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_MLOCK_ALLOCATOR_H_ +#define BOTAN_MLOCK_ALLOCATOR_H_ + +#include <botan/types.h> +#include <vector> +#include <memory> + +BOTAN_FUTURE_INTERNAL_HEADER(locking_allocator.h) + +namespace Botan { + +class Memory_Pool; + +class BOTAN_PUBLIC_API(2,0) mlock_allocator final + { + public: + static mlock_allocator& instance(); + + void* allocate(size_t num_elems, size_t elem_size); + + bool deallocate(void* p, size_t num_elems, size_t elem_size) noexcept; + + mlock_allocator(const mlock_allocator&) = delete; + + mlock_allocator& operator=(const mlock_allocator&) = delete; + + private: + mlock_allocator(); + + ~mlock_allocator(); + + std::unique_ptr<Memory_Pool> m_pool; + std::vector<void*> m_locked_pages; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/mem_ops.cpp b/comm/third_party/botan/src/lib/utils/mem_ops.cpp new file mode 100644 index 0000000000..9ef1d6c4e3 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/mem_ops.cpp @@ -0,0 +1,68 @@ +/* +* (C) 2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/mem_ops.h> +#include <botan/internal/ct_utils.h> +#include <cstdlib> +#include <new> + +#if defined(BOTAN_HAS_LOCKING_ALLOCATOR) + #include <botan/locking_allocator.h> +#endif + +namespace Botan { + +BOTAN_MALLOC_FN void* allocate_memory(size_t elems, size_t elem_size) + { + if(elems == 0 || elem_size == 0) + return nullptr; + +#if defined(BOTAN_HAS_LOCKING_ALLOCATOR) + if(void* p = mlock_allocator::instance().allocate(elems, elem_size)) + return p; +#endif + + void* ptr = std::calloc(elems, elem_size); + if(!ptr) + throw std::bad_alloc(); + return ptr; + } + +void deallocate_memory(void* p, size_t elems, size_t elem_size) + { + if(p == nullptr) + return; + + secure_scrub_memory(p, elems * elem_size); + +#if defined(BOTAN_HAS_LOCKING_ALLOCATOR) + if(mlock_allocator::instance().deallocate(p, elems, elem_size)) + return; +#endif + + std::free(p); + } + +void initialize_allocator() + { +#if defined(BOTAN_HAS_LOCKING_ALLOCATOR) + mlock_allocator::instance(); +#endif + } + +uint8_t ct_compare_u8(const uint8_t x[], + const uint8_t y[], + size_t len) + { + volatile uint8_t difference = 0; + + for(size_t i = 0; i != len; ++i) + difference |= (x[i] ^ y[i]); + + return CT::Mask<uint8_t>::is_zero(difference).value(); + } + +} diff --git a/comm/third_party/botan/src/lib/utils/mem_ops.h b/comm/third_party/botan/src/lib/utils/mem_ops.h new file mode 100644 index 0000000000..deb4c01f28 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/mem_ops.h @@ -0,0 +1,365 @@ +/* +* Memory Operations +* (C) 1999-2009,2012,2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_MEMORY_OPS_H_ +#define BOTAN_MEMORY_OPS_H_ + +#include <botan/types.h> +#include <cstring> +#include <type_traits> +#include <vector> + +namespace Botan { + +/** +* Allocate a memory buffer by some method. This should only be used for +* primitive types (uint8_t, uint32_t, etc). +* +* @param elems the number of elements +* @param elem_size the size of each element +* @return pointer to allocated and zeroed memory, or throw std::bad_alloc on failure +*/ +BOTAN_PUBLIC_API(2,3) BOTAN_MALLOC_FN void* allocate_memory(size_t elems, size_t elem_size); + +/** +* Free a pointer returned by allocate_memory +* @param p the pointer returned by allocate_memory +* @param elems the number of elements, as passed to allocate_memory +* @param elem_size the size of each element, as passed to allocate_memory +*/ +BOTAN_PUBLIC_API(2,3) void deallocate_memory(void* p, size_t elems, size_t elem_size); + +/** +* Ensure the allocator is initialized +*/ +void BOTAN_UNSTABLE_API initialize_allocator(); + +class Allocator_Initializer + { + public: + Allocator_Initializer() { initialize_allocator(); } + }; + +/** +* Scrub memory contents in a way that a compiler should not elide, +* using some system specific technique. Note that this function might +* not zero the memory (for example, in some hypothetical +* implementation it might combine the memory contents with the output +* of a system PRNG), but if you can detect any difference in behavior +* at runtime then the clearing is side-effecting and you can just +* use `clear_mem`. +* +* Use this function to scrub memory just before deallocating it, or on +* a stack buffer before returning from the function. +* +* @param ptr a pointer to memory to scrub +* @param n the number of bytes pointed to by ptr +*/ +BOTAN_PUBLIC_API(2,0) void secure_scrub_memory(void* ptr, size_t n); + +/** +* Memory comparison, input insensitive +* @param x a pointer to an array +* @param y a pointer to another array +* @param len the number of Ts in x and y +* @return 0xFF iff x[i] == y[i] forall i in [0...n) or 0x00 otherwise +*/ +BOTAN_PUBLIC_API(2,9) uint8_t ct_compare_u8(const uint8_t x[], + const uint8_t y[], + size_t len); + +/** +* Memory comparison, input insensitive +* @param x a pointer to an array +* @param y a pointer to another array +* @param len the number of Ts in x and y +* @return true iff x[i] == y[i] forall i in [0...n) +*/ +inline bool constant_time_compare(const uint8_t x[], + const uint8_t y[], + size_t len) + { + return ct_compare_u8(x, y, len) == 0xFF; + } + +/** +* Zero out some bytes. Warning: use secure_scrub_memory instead if the +* memory is about to be freed or otherwise the compiler thinks it can +* elide the writes. +* +* @param ptr a pointer to memory to zero +* @param bytes the number of bytes to zero in ptr +*/ +inline void clear_bytes(void* ptr, size_t bytes) + { + if(bytes > 0) + { + std::memset(ptr, 0, bytes); + } + } + +/** +* Zero memory before use. This simply calls memset and should not be +* used in cases where the compiler cannot see the call as a +* side-effecting operation (for example, if calling clear_mem before +* deallocating memory, the compiler would be allowed to omit the call +* to memset entirely under the as-if rule.) +* +* @param ptr a pointer to an array of Ts to zero +* @param n the number of Ts pointed to by ptr +*/ +template<typename T> inline void clear_mem(T* ptr, size_t n) + { + clear_bytes(ptr, sizeof(T)*n); + } + +// is_trivially_copyable is missing in g++ < 5.0 +#if (BOTAN_GCC_VERSION > 0 && BOTAN_GCC_VERSION < 500) +#define BOTAN_IS_TRIVIALLY_COPYABLE(T) true +#else +#define BOTAN_IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable<T>::value +#endif + +/** +* Copy memory +* @param out the destination array +* @param in the source array +* @param n the number of elements of in/out +*/ +template<typename T> inline void copy_mem(T* out, const T* in, size_t n) + { + static_assert(std::is_trivial<typename std::decay<T>::type>::value, ""); + BOTAN_ASSERT_IMPLICATION(n > 0, in != nullptr && out != nullptr, + "If n > 0 then args are not null"); + + if(in != nullptr && out != nullptr && n > 0) + { + std::memmove(out, in, sizeof(T)*n); + } + } + +template<typename T> inline void typecast_copy(uint8_t out[], T in[], size_t N) + { + static_assert(BOTAN_IS_TRIVIALLY_COPYABLE(T), ""); + std::memcpy(out, in, sizeof(T)*N); + } + +template<typename T> inline void typecast_copy(T out[], const uint8_t in[], size_t N) + { + static_assert(std::is_trivial<T>::value, ""); + std::memcpy(out, in, sizeof(T)*N); + } + +template<typename T> inline void typecast_copy(uint8_t out[], T in) + { + typecast_copy(out, &in, 1); + } + +template<typename T> inline void typecast_copy(T& out, const uint8_t in[]) + { + static_assert(std::is_trivial<typename std::decay<T>::type>::value, ""); + typecast_copy(&out, in, 1); + } + +template <class To, class From> inline To typecast_copy(const From *src) noexcept + { + static_assert(BOTAN_IS_TRIVIALLY_COPYABLE(From) && std::is_trivial<To>::value, ""); + To dst; + std::memcpy(&dst, src, sizeof(To)); + return dst; + } + +/** +* Set memory to a fixed value +* @param ptr a pointer to an array of bytes +* @param n the number of Ts pointed to by ptr +* @param val the value to set each byte to +*/ +inline void set_mem(uint8_t* ptr, size_t n, uint8_t val) + { + if(n > 0) + { + std::memset(ptr, val, n); + } + } + +inline const uint8_t* cast_char_ptr_to_uint8(const char* s) + { + return reinterpret_cast<const uint8_t*>(s); + } + +inline const char* cast_uint8_ptr_to_char(const uint8_t* b) + { + return reinterpret_cast<const char*>(b); + } + +inline uint8_t* cast_char_ptr_to_uint8(char* s) + { + return reinterpret_cast<uint8_t*>(s); + } + +inline char* cast_uint8_ptr_to_char(uint8_t* b) + { + return reinterpret_cast<char*>(b); + } + +/** +* Memory comparison, input insensitive +* @param p1 a pointer to an array +* @param p2 a pointer to another array +* @param n the number of Ts in p1 and p2 +* @return true iff p1[i] == p2[i] forall i in [0...n) +*/ +template<typename T> inline bool same_mem(const T* p1, const T* p2, size_t n) + { + volatile T difference = 0; + + for(size_t i = 0; i != n; ++i) + difference |= (p1[i] ^ p2[i]); + + return difference == 0; + } + +template<typename T, typename Alloc> +size_t buffer_insert(std::vector<T, Alloc>& buf, + size_t buf_offset, + const T input[], + size_t input_length) + { + BOTAN_ASSERT_NOMSG(buf_offset <= buf.size()); + const size_t to_copy = std::min(input_length, buf.size() - buf_offset); + if(to_copy > 0) + { + copy_mem(&buf[buf_offset], input, to_copy); + } + return to_copy; + } + +template<typename T, typename Alloc, typename Alloc2> +size_t buffer_insert(std::vector<T, Alloc>& buf, + size_t buf_offset, + const std::vector<T, Alloc2>& input) + { + BOTAN_ASSERT_NOMSG(buf_offset <= buf.size()); + const size_t to_copy = std::min(input.size(), buf.size() - buf_offset); + if(to_copy > 0) + { + copy_mem(&buf[buf_offset], input.data(), to_copy); + } + return to_copy; + } + +/** +* XOR arrays. Postcondition out[i] = in[i] ^ out[i] forall i = 0...length +* @param out the input/output buffer +* @param in the read-only input buffer +* @param length the length of the buffers +*/ +inline void xor_buf(uint8_t out[], + const uint8_t in[], + size_t length) + { + const size_t blocks = length - (length % 32); + + for(size_t i = 0; i != blocks; i += 32) + { + uint64_t x[4]; + uint64_t y[4]; + + typecast_copy(x, out + i, 4); + typecast_copy(y, in + i, 4); + + x[0] ^= y[0]; + x[1] ^= y[1]; + x[2] ^= y[2]; + x[3] ^= y[3]; + + typecast_copy(out + i, x, 4); + } + + for(size_t i = blocks; i != length; ++i) + { + out[i] ^= in[i]; + } + } + +/** +* XOR arrays. Postcondition out[i] = in[i] ^ in2[i] forall i = 0...length +* @param out the output buffer +* @param in the first input buffer +* @param in2 the second output buffer +* @param length the length of the three buffers +*/ +inline void xor_buf(uint8_t out[], + const uint8_t in[], + const uint8_t in2[], + size_t length) + { + const size_t blocks = length - (length % 32); + + for(size_t i = 0; i != blocks; i += 32) + { + uint64_t x[4]; + uint64_t y[4]; + + typecast_copy(x, in + i, 4); + typecast_copy(y, in2 + i, 4); + + x[0] ^= y[0]; + x[1] ^= y[1]; + x[2] ^= y[2]; + x[3] ^= y[3]; + + typecast_copy(out + i, x, 4); + } + + for(size_t i = blocks; i != length; ++i) + { + out[i] = in[i] ^ in2[i]; + } + } + +template<typename Alloc, typename Alloc2> +void xor_buf(std::vector<uint8_t, Alloc>& out, + const std::vector<uint8_t, Alloc2>& in, + size_t n) + { + xor_buf(out.data(), in.data(), n); + } + +template<typename Alloc> +void xor_buf(std::vector<uint8_t, Alloc>& out, + const uint8_t* in, + size_t n) + { + xor_buf(out.data(), in, n); + } + +template<typename Alloc, typename Alloc2> +void xor_buf(std::vector<uint8_t, Alloc>& out, + const uint8_t* in, + const std::vector<uint8_t, Alloc2>& in2, + size_t n) + { + xor_buf(out.data(), in, in2.data(), n); + } + +template<typename Alloc, typename Alloc2> +std::vector<uint8_t, Alloc>& +operator^=(std::vector<uint8_t, Alloc>& out, + const std::vector<uint8_t, Alloc2>& in) + { + if(out.size() < in.size()) + out.resize(in.size()); + + xor_buf(out.data(), in.data(), in.size()); + return out; + } + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/mem_pool/info.txt b/comm/third_party/botan/src/lib/utils/mem_pool/info.txt new file mode 100644 index 0000000000..a8a5a53e1b --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/mem_pool/info.txt @@ -0,0 +1,7 @@ +<defines> +MEM_POOL -> 20180309 +</defines> + +<header:internal> +mem_pool.h +</header:internal> diff --git a/comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.cpp b/comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.cpp new file mode 100644 index 0000000000..1ff3f0759b --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.cpp @@ -0,0 +1,435 @@ +/* +* (C) 2018,2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/mem_pool.h> +#include <botan/mem_ops.h> +#include <algorithm> + +#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS) + #include <botan/internal/os_utils.h> +#endif + +namespace Botan { + +/* +* Memory pool theory of operation +* +* This allocator is not useful for general purpose but works well within the +* context of allocating cryptographic keys. It makes several assumptions which +* don't work for implementing malloc but simplify and speed up the implementation: +* +* - There is some set of pages, which cannot be expanded later. These are pages +* which were allocated, mlocked and passed to the Memory_Pool constructor. +* +* - The allocator is allowed to return null anytime it feels like not servicing +* a request, in which case the request will be sent to calloc instead. In +* particular, requests which are too small or too large are rejected. +* +* - Most allocations are powers of 2, the remainder are usually a multiple of 8 +* +* - Free requests include the size of the allocation, so there is no need to +* track this within the pool. +* +* - Alignment is important to the caller. For this allocator, any allocation of +* size N is aligned evenly at N bytes. +* +* Initially each page is in the free page list. Each page is used for just one +* size of allocation, with requests bucketed into a small number of common +* sizes. If the allocation would be too big or too small it is rejected by the pool. +* +* The free list is maintained by a bitmap, one per page/Bucket. Since each +* Bucket only maintains objects of a single size, each bit set or clear +* indicates the status of one object. +* +* An allocation walks the list of buckets and asks each in turn if there is +* space. If a Bucket does not have any space, it sets a boolean flag m_is_full +* so that it does not need to rescan when asked again. The flag is cleared on +* first free from that bucket. If no bucket has space, but there are some free +* pages left, a free page is claimed as a new Bucket for that size. In this case +* it is pushed to the front of the list so it is first in line to service new +* requests. +* +* A deallocation also walks the list of buckets for the size and asks each +* Bucket in turn if it recognizes the pointer. When a Bucket becomes empty as a +* result of a deallocation, it is recycled back into the free pool. When this +* happens, the Buckets page goes to the end of the free list. All pages on the +* free list are marked in the MMU as noaccess, so anything touching them will +* immediately crash. They are only marked R/W once placed into a new bucket. +* Making the free list FIFO maximizes the time between the last free of a bucket +* and that page being writable again, maximizing chances of crashing after a +* use-after-free. +* +* Future work +* ------------- +* +* The allocator is protected by a global lock. It would be good to break this +* up, since almost all of the work can actually be done in parallel especially +* when allocating objects of different sizes (which can't possibly share a +* bucket). +* +* It may be worthwhile to optimize deallocation by storing the Buckets in order +* (by pointer value) which would allow binary search to find the owning bucket. +* +* A useful addition would be to randomize the allocations. Memory_Pool would be +* changed to receive also a RandomNumberGenerator& object (presumably the system +* RNG, or maybe a ChaCha_RNG seeded with system RNG). Then the bucket to use and +* the offset within the bucket would be chosen randomly, instead of using first fit. +* +* Right now we don't make any provision for threading, so if two threads both +* allocate 32 byte values one after the other, the two allocations will likely +* share a cache line. Ensuring that distinct threads will (tend to) use distinct +* buckets would reduce this. +* +* Supporting a realloc-style API may be useful. +*/ + +namespace { + +size_t choose_bucket(size_t n) + { + const size_t MINIMUM_ALLOCATION = 16; + const size_t MAXIMUM_ALLOCATION = 256; + + if(n < MINIMUM_ALLOCATION || n > MAXIMUM_ALLOCATION) + return 0; + + // Need to tune these + + const size_t buckets[] = { + 16, 24, 32, 48, 64, 80, 96, 112, 128, 160, 192, 256, 0, + }; + + for(size_t i = 0; buckets[i]; ++i) + { + if(n <= buckets[i]) + { + return buckets[i]; + } + } + + return 0; + } + +inline bool ptr_in_pool(const void* pool_ptr, size_t poolsize, + const void* buf_ptr, size_t bufsize) + { + const uintptr_t pool = reinterpret_cast<uintptr_t>(pool_ptr); + const uintptr_t buf = reinterpret_cast<uintptr_t>(buf_ptr); + return (buf >= pool) && (buf + bufsize <= pool + poolsize); + } + +// return index of first set bit +template<typename T> +size_t find_set_bit(T b) + { + size_t s = 8*sizeof(T) / 2; + size_t bit = 0; + + // In this context we don't need to be const-time + while(s > 0) + { + const T mask = (static_cast<T>(1) << s) - 1; + if((b & mask) == 0) + { + bit += s; + b >>= s; + } + s /= 2; + } + + return bit; + } + +class BitMap final + { + public: + BitMap(size_t bits) : m_len(bits) + { + m_bits.resize((bits + BITMASK_BITS - 1) / BITMASK_BITS); + m_main_mask = static_cast<bitmask_type>(~0); + m_last_mask = m_main_mask; + + if(bits % BITMASK_BITS != 0) + m_last_mask = (static_cast<bitmask_type>(1) << (bits % BITMASK_BITS)) - 1; + } + + bool find_free(size_t* bit); + + void free(size_t bit) + { + BOTAN_ASSERT_NOMSG(bit <= m_len); + const size_t w = bit / BITMASK_BITS; + BOTAN_ASSERT_NOMSG(w < m_bits.size()); + const bitmask_type mask = static_cast<bitmask_type>(1) << (bit % BITMASK_BITS); + m_bits[w] = m_bits[w] & (~mask); + } + + bool empty() const + { + for(size_t i = 0; i != m_bits.size(); ++i) + { + if(m_bits[i] != 0) + { + return false; + } + } + + return true; + } + + private: +#if defined(BOTAN_ENABLE_DEBUG_ASSERTS) + typedef uint8_t bitmask_type; + enum { BITMASK_BITS = 8 }; +#else + typedef word bitmask_type; + enum { BITMASK_BITS = BOTAN_MP_WORD_BITS }; +#endif + + size_t m_len; + bitmask_type m_main_mask; + bitmask_type m_last_mask; + std::vector<bitmask_type> m_bits; + }; + +bool BitMap::find_free(size_t* bit) + { + for(size_t i = 0; i != m_bits.size(); ++i) + { + const bitmask_type mask = (i == m_bits.size() - 1) ? m_last_mask : m_main_mask; + if((m_bits[i] & mask) != mask) + { + size_t free_bit = find_set_bit(~m_bits[i]); + const bitmask_type bmask = static_cast<bitmask_type>(1) << (free_bit % BITMASK_BITS); + BOTAN_ASSERT_NOMSG((m_bits[i] & bmask) == 0); + m_bits[i] |= bmask; + *bit = BITMASK_BITS*i + free_bit; + return true; + } + } + + return false; + } + +} + +class Bucket final + { + public: + Bucket(uint8_t* mem, size_t mem_size, size_t item_size) : + m_item_size(item_size), + m_page_size(mem_size), + m_range(mem), + m_bitmap(mem_size / item_size), + m_is_full(false) + { + } + + uint8_t* alloc() + { + if(m_is_full) + { + // I know I am full + return nullptr; + } + + size_t offset; + if(!m_bitmap.find_free(&offset)) + { + // I just found out I am full + m_is_full = true; + return nullptr; + } + + BOTAN_ASSERT(offset * m_item_size < m_page_size, "Offset is in range"); + return m_range + m_item_size*offset; + } + + bool free(void* p) + { + if(!in_this_bucket(p)) + return false; + + /* + Zero also any trailing bytes, which should not have been written to, + but maybe the user was bad and wrote past the end. + */ + std::memset(p, 0, m_item_size); + + const size_t offset = (reinterpret_cast<uintptr_t>(p) - reinterpret_cast<uintptr_t>(m_range)) / m_item_size; + + m_bitmap.free(offset); + m_is_full = false; + + return true; + } + + bool in_this_bucket(void* p) const + { + return ptr_in_pool(m_range, m_page_size, p, m_item_size); + } + + bool empty() const + { + return m_bitmap.empty(); + } + + uint8_t* ptr() const + { + return m_range; + } + + private: + size_t m_item_size; + size_t m_page_size; + uint8_t* m_range; + BitMap m_bitmap; + bool m_is_full; + }; + +Memory_Pool::Memory_Pool(const std::vector<void*>& pages, size_t page_size) : + m_page_size(page_size) + { + m_min_page_ptr = ~static_cast<uintptr_t>(0); + m_max_page_ptr = 0; + + for(size_t i = 0; i != pages.size(); ++i) + { + const uintptr_t p = reinterpret_cast<uintptr_t>(pages[i]); + + m_min_page_ptr = std::min(p, m_min_page_ptr); + m_max_page_ptr = std::max(p, m_max_page_ptr); + + clear_bytes(pages[i], m_page_size); +#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS) + OS::page_prohibit_access(pages[i]); +#endif + m_free_pages.push_back(static_cast<uint8_t*>(pages[i])); + } + + /* + Right now this points to the start of the last page, adjust it to instead + point to the first byte of the following page + */ + m_max_page_ptr += page_size; + } + +Memory_Pool::~Memory_Pool() + { +#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS) + for(size_t i = 0; i != m_free_pages.size(); ++i) + { + OS::page_allow_access(m_free_pages[i]); + } +#endif + } + +void* Memory_Pool::allocate(size_t n) + { + if(n > m_page_size) + return nullptr; + + const size_t n_bucket = choose_bucket(n); + + if(n_bucket > 0) + { + lock_guard_type<mutex_type> lock(m_mutex); + + std::deque<Bucket>& buckets = m_buckets_for[n_bucket]; + + /* + It would be optimal to pick the bucket with the most usage, + since a bucket with say 1 item allocated out of it has a high + chance of becoming later freed and then the whole page can be + recycled. + */ + for(auto& bucket : buckets) + { + if(uint8_t* p = bucket.alloc()) + return p; + + // If the bucket is full, maybe move it to the end of the list? + // Otoh bucket search should be very fast + } + + if(m_free_pages.size() > 0) + { + uint8_t* ptr = m_free_pages[0]; + m_free_pages.pop_front(); +#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS) + OS::page_allow_access(ptr); +#endif + buckets.push_front(Bucket(ptr, m_page_size, n_bucket)); + void* p = buckets[0].alloc(); + BOTAN_ASSERT_NOMSG(p != nullptr); + return p; + } + } + + // out of room + return nullptr; + } + +bool Memory_Pool::deallocate(void* p, size_t len) noexcept + { + // Do a fast range check first, before taking the lock + const uintptr_t p_val = reinterpret_cast<uintptr_t>(p); + if(p_val < m_min_page_ptr || p_val > m_max_page_ptr) + return false; + + const size_t n_bucket = choose_bucket(len); + + if(n_bucket != 0) + { + try + { + lock_guard_type<mutex_type> lock(m_mutex); + + std::deque<Bucket>& buckets = m_buckets_for[n_bucket]; + + for(size_t i = 0; i != buckets.size(); ++i) + { + Bucket& bucket = buckets[i]; + if(bucket.free(p)) + { + if(bucket.empty()) + { +#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS) + OS::page_prohibit_access(bucket.ptr()); +#endif + m_free_pages.push_back(bucket.ptr()); + + if(i != buckets.size() - 1) + std::swap(buckets.back(), buckets[i]); + buckets.pop_back(); + } + return true; + } + } + } + catch(...) + { + /* + * The only exception throws that can occur in the above code are from + * either the STL or BOTAN_ASSERT failures. In either case, such an + * error indicates a logic error or data corruption in the memory + * allocator such that it is no longer safe to continue executing. + * + * Since this function is noexcept, simply letting the exception escape + * is sufficient for terminate to be called. However in this scenario + * it is implementation defined if any stack unwinding is performed. + * Since stack unwinding could cause further memory deallocations this + * could result in further corruption in this allocator state. To prevent + * this, call terminate directly. + */ + std::terminate(); + } + } + + return false; + } + +} diff --git a/comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.h b/comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.h new file mode 100644 index 0000000000..4fcec15eeb --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.h @@ -0,0 +1,58 @@ +/* +* (C) 2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_MEM_POOL_H_ +#define BOTAN_MEM_POOL_H_ + +#include <botan/types.h> +#include <botan/mutex.h> +#include <vector> +#include <deque> +#include <map> + +namespace Botan { + +class Bucket; + +class BOTAN_TEST_API Memory_Pool final + { + public: + /** + * Initialize a memory pool. The memory is not owned by *this, + * it must be freed by the caller. + * @param pages a list of pages to allocate from + * @param page_size the system page size, each page should + * point to exactly this much memory. + */ + Memory_Pool(const std::vector<void*>& pages, + size_t page_size); + + ~Memory_Pool(); + + void* allocate(size_t size); + + bool deallocate(void* p, size_t size) noexcept; + + Memory_Pool(const Memory_Pool&) = delete; + Memory_Pool(Memory_Pool&&) = delete; + + Memory_Pool& operator=(const Memory_Pool&) = delete; + Memory_Pool& operator=(Memory_Pool&&) = delete; + + private: + const size_t m_page_size = 0; + + mutex_type m_mutex; + + std::deque<uint8_t*> m_free_pages; + std::map<size_t, std::deque<Bucket>> m_buckets_for; + uintptr_t m_min_page_ptr; + uintptr_t m_max_page_ptr; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/mul128.h b/comm/third_party/botan/src/lib/utils/mul128.h new file mode 100644 index 0000000000..8cdaae21e0 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/mul128.h @@ -0,0 +1,125 @@ +/* +* 64x64->128 bit multiply operation +* (C) 2013,2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_UTIL_MUL128_H_ +#define BOTAN_UTIL_MUL128_H_ + +#include <botan/types.h> + +BOTAN_FUTURE_INTERNAL_HEADER(mul128.h) + +namespace Botan { + +#if defined(__SIZEOF_INT128__) && defined(BOTAN_TARGET_CPU_HAS_NATIVE_64BIT) + #define BOTAN_TARGET_HAS_NATIVE_UINT128 + + // Prefer TI mode over __int128 as GCC rejects the latter in pendantic mode + #if defined(__GNUG__) + typedef unsigned int uint128_t __attribute__((mode(TI))); + #else + typedef unsigned __int128 uint128_t; + #endif +#endif + +} + +#if defined(BOTAN_TARGET_HAS_NATIVE_UINT128) + +#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) \ + do { \ + const uint128_t r = static_cast<uint128_t>(a) * b; \ + *hi = (r >> 64) & 0xFFFFFFFFFFFFFFFF; \ + *lo = (r ) & 0xFFFFFFFFFFFFFFFF; \ + } while(0) + +#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC) && defined(BOTAN_TARGET_CPU_HAS_NATIVE_64BIT) + +#include <intrin.h> +#pragma intrinsic(_umul128) + +#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) \ + do { *lo = _umul128(a, b, hi); } while(0) + +#elif defined(BOTAN_USE_GCC_INLINE_ASM) + +#if defined(BOTAN_TARGET_ARCH_IS_X86_64) + +#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) do { \ + asm("mulq %3" : "=d" (*hi), "=a" (*lo) : "a" (a), "rm" (b) : "cc"); \ + } while(0) + +#elif defined(BOTAN_TARGET_ARCH_IS_ALPHA) + +#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) do { \ + asm("umulh %1,%2,%0" : "=r" (*hi) : "r" (a), "r" (b)); \ + *lo = a * b; \ +} while(0) + +#elif defined(BOTAN_TARGET_ARCH_IS_IA64) + +#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) do { \ + asm("xmpy.hu %0=%1,%2" : "=f" (*hi) : "f" (a), "f" (b)); \ + *lo = a * b; \ +} while(0) + +#elif defined(BOTAN_TARGET_ARCH_IS_PPC64) + +#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) do { \ + asm("mulhdu %0,%1,%2" : "=r" (*hi) : "r" (a), "r" (b) : "cc"); \ + *lo = a * b; \ +} while(0) + +#endif + +#endif + +namespace Botan { + +/** +* Perform a 64x64->128 bit multiplication +*/ +inline void mul64x64_128(uint64_t a, uint64_t b, uint64_t* lo, uint64_t* hi) + { +#if defined(BOTAN_FAST_64X64_MUL) + BOTAN_FAST_64X64_MUL(a, b, lo, hi); +#else + + /* + * Do a 64x64->128 multiply using four 32x32->64 multiplies plus + * some adds and shifts. Last resort for CPUs like UltraSPARC (with + * 64-bit registers/ALU, but no 64x64->128 multiply) or 32-bit CPUs. + */ + const size_t HWORD_BITS = 32; + const uint32_t HWORD_MASK = 0xFFFFFFFF; + + const uint32_t a_hi = (a >> HWORD_BITS); + const uint32_t a_lo = (a & HWORD_MASK); + const uint32_t b_hi = (b >> HWORD_BITS); + const uint32_t b_lo = (b & HWORD_MASK); + + uint64_t x0 = static_cast<uint64_t>(a_hi) * b_hi; + uint64_t x1 = static_cast<uint64_t>(a_lo) * b_hi; + uint64_t x2 = static_cast<uint64_t>(a_hi) * b_lo; + uint64_t x3 = static_cast<uint64_t>(a_lo) * b_lo; + + // this cannot overflow as (2^32-1)^2 + 2^32-1 < 2^64-1 + x2 += x3 >> HWORD_BITS; + + // this one can overflow + x2 += x1; + + // propagate the carry if any + x0 += static_cast<uint64_t>(static_cast<bool>(x2 < x1)) << HWORD_BITS; + + *hi = x0 + (x2 >> HWORD_BITS); + *lo = ((x2 & HWORD_MASK) << HWORD_BITS) + (x3 & HWORD_MASK); +#endif + } + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/mutex.h b/comm/third_party/botan/src/lib/utils/mutex.h new file mode 100644 index 0000000000..a230af9888 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/mutex.h @@ -0,0 +1,60 @@ +/* +* (C) 2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_UTIL_MUTEX_H_ +#define BOTAN_UTIL_MUTEX_H_ + +#include <botan/types.h> + +#if defined(BOTAN_TARGET_OS_HAS_THREADS) + +#include <mutex> + +namespace Botan { + +template<typename T> using lock_guard_type = std::lock_guard<T>; +typedef std::mutex mutex_type; +typedef std::recursive_mutex recursive_mutex_type; + +} + +#else + +// No threads + +namespace Botan { + +template<typename Mutex> +class lock_guard final + { + public: + explicit lock_guard(Mutex& m) : m_mutex(m) + { m_mutex.lock(); } + + ~lock_guard() { m_mutex.unlock(); } + + lock_guard(const lock_guard& other) = delete; + lock_guard& operator=(const lock_guard& other) = delete; + private: + Mutex& m_mutex; + }; + +class noop_mutex final + { + public: + void lock() {} + void unlock() {} + }; + +typedef noop_mutex mutex_type; +typedef noop_mutex recursive_mutex_type; +template<typename T> using lock_guard_type = lock_guard<T>; + +} + +#endif + +#endif diff --git a/comm/third_party/botan/src/lib/utils/os_utils.cpp b/comm/third_party/botan/src/lib/utils/os_utils.cpp new file mode 100644 index 0000000000..f5151da42b --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/os_utils.cpp @@ -0,0 +1,757 @@ +/* +* OS and machine specific utility functions +* (C) 2015,2016,2017,2018 Jack Lloyd +* (C) 2016 Daniel Neus +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/os_utils.h> +#include <botan/cpuid.h> +#include <botan/exceptn.h> +#include <botan/mem_ops.h> + +#include <algorithm> +#include <chrono> +#include <cstdlib> + +#if defined(BOTAN_TARGET_OS_HAS_THREADS) + #include <thread> +#endif + +#if defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO) + #include <string.h> +#endif + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + #include <sys/types.h> + #include <sys/resource.h> + #include <sys/mman.h> + #include <signal.h> + #include <stdlib.h> + #include <setjmp.h> + #include <unistd.h> + #include <errno.h> + #include <termios.h> + #undef B0 +#endif + +#if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN) + #include <emscripten/emscripten.h> +#endif + +#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_IS_ANDROID) || \ + defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO) + #include <sys/auxv.h> +#endif + +#if defined(BOTAN_TARGET_OS_HAS_WIN32) + #define NOMINMAX 1 + #define _WINSOCKAPI_ // stop windows.h including winsock.h + #include <windows.h> +#endif + +#if defined(BOTAN_TARGET_OS_IS_ANDROID) + #include <elf.h> + extern "C" char **environ; +#endif + +#if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS) + #include <mach/vm_statistics.h> +#endif + +namespace Botan { + +// Not defined in OS namespace for historical reasons +void secure_scrub_memory(void* ptr, size_t n) + { +#if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY) + ::RtlSecureZeroMemory(ptr, n); + +#elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO) + ::explicit_bzero(ptr, n); + +#elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_MEMSET) + (void)::explicit_memset(ptr, 0, n); + +#elif defined(BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO) && (BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO == 1) + /* + Call memset through a static volatile pointer, which the compiler + should not elide. This construct should be safe in conforming + compilers, but who knows. I did confirm that on x86-64 GCC 6.1 and + Clang 3.8 both create code that saves the memset address in the + data segment and unconditionally loads and jumps to that address. + */ + static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset; + (memset_ptr)(ptr, 0, n); +#else + + volatile uint8_t* p = reinterpret_cast<volatile uint8_t*>(ptr); + + for(size_t i = 0; i != n; ++i) + p[i] = 0; +#endif + } + +uint32_t OS::get_process_id() + { +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + return ::getpid(); +#elif defined(BOTAN_TARGET_OS_HAS_WIN32) + return ::GetCurrentProcessId(); +#elif defined(BOTAN_TARGET_OS_IS_INCLUDEOS) || defined(BOTAN_TARGET_OS_IS_LLVM) || defined(BOTAN_TARGET_OS_IS_NONE) + return 0; // truly no meaningful value +#else + #error "Missing get_process_id" +#endif + } + +unsigned long OS::get_auxval(unsigned long id) + { +#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) + return ::getauxval(id); +#elif defined(BOTAN_TARGET_OS_IS_ANDROID) && defined(BOTAN_TARGET_ARCH_IS_ARM32) + + if(id == 0) + return 0; + + char **p = environ; + + while(*p++ != nullptr) + ; + + Elf32_auxv_t *e = reinterpret_cast<Elf32_auxv_t*>(p); + + while(e != nullptr) + { + if(e->a_type == id) + return e->a_un.a_val; + e++; + } + + return 0; +#elif defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO) + unsigned long auxinfo = 0; + ::elf_aux_info(id, &auxinfo, sizeof(auxinfo)); + return auxinfo; +#else + BOTAN_UNUSED(id); + return 0; +#endif + } + +bool OS::running_in_privileged_state() + { +#if defined(AT_SECURE) + return OS::get_auxval(AT_SECURE) != 0; +#elif defined(BOTAN_TARGET_OS_HAS_POSIX1) + return (::getuid() != ::geteuid()) || (::getgid() != ::getegid()); +#else + return false; +#endif + } + +uint64_t OS::get_cpu_cycle_counter() + { + uint64_t rtc = 0; + +#if defined(BOTAN_TARGET_OS_HAS_WIN32) + LARGE_INTEGER tv; + ::QueryPerformanceCounter(&tv); + rtc = tv.QuadPart; + +#elif defined(BOTAN_USE_GCC_INLINE_ASM) + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + + if(CPUID::has_rdtsc()) + { + uint32_t rtc_low = 0, rtc_high = 0; + asm volatile("rdtsc" : "=d" (rtc_high), "=a" (rtc_low)); + rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low; + } + +#elif defined(BOTAN_TARGET_ARCH_IS_PPC64) + + for(;;) + { + uint32_t rtc_low = 0, rtc_high = 0, rtc_high2 = 0; + asm volatile("mftbu %0" : "=r" (rtc_high)); + asm volatile("mftb %0" : "=r" (rtc_low)); + asm volatile("mftbu %0" : "=r" (rtc_high2)); + + if(rtc_high == rtc_high2) + { + rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low; + break; + } + } + +#elif defined(BOTAN_TARGET_ARCH_IS_ALPHA) + asm volatile("rpcc %0" : "=r" (rtc)); + + // OpenBSD does not trap access to the %tick register +#elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD) + asm volatile("rd %%tick, %0" : "=r" (rtc)); + +#elif defined(BOTAN_TARGET_ARCH_IS_IA64) + asm volatile("mov %0=ar.itc" : "=r" (rtc)); + +#elif defined(BOTAN_TARGET_ARCH_IS_S390X) + asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc"); + +#elif defined(BOTAN_TARGET_ARCH_IS_HPPA) + asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only? + +#else + //#warning "OS::get_cpu_cycle_counter not implemented" +#endif + +#endif + + return rtc; + } + +size_t OS::get_cpu_total() + { +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(_SC_NPROCESSORS_CONF) + const long res = ::sysconf(_SC_NPROCESSORS_CONF); + if(res > 0) + return static_cast<size_t>(res); +#endif + +#if defined(BOTAN_TARGET_OS_HAS_THREADS) + return static_cast<size_t>(std::thread::hardware_concurrency()); +#else + return 1; +#endif + } + +size_t OS::get_cpu_available() + { +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(_SC_NPROCESSORS_ONLN) + const long res = ::sysconf(_SC_NPROCESSORS_ONLN); + if(res > 0) + return static_cast<size_t>(res); +#endif + + return OS::get_cpu_total(); + } + +uint64_t OS::get_high_resolution_clock() + { + if(uint64_t cpu_clock = OS::get_cpu_cycle_counter()) + return cpu_clock; + +#if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN) + return emscripten_get_now(); +#endif + + /* + If we got here either we either don't have an asm instruction + above, or (for x86) RDTSC is not available at runtime. Try some + clock_gettimes and return the first one that works, or otherwise + fall back to std::chrono. + */ + +#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME) + + // The ordering here is somewhat arbitrary... + const clockid_t clock_types[] = { +#if defined(CLOCK_MONOTONIC_HR) + CLOCK_MONOTONIC_HR, +#endif +#if defined(CLOCK_MONOTONIC_RAW) + CLOCK_MONOTONIC_RAW, +#endif +#if defined(CLOCK_MONOTONIC) + CLOCK_MONOTONIC, +#endif +#if defined(CLOCK_PROCESS_CPUTIME_ID) + CLOCK_PROCESS_CPUTIME_ID, +#endif +#if defined(CLOCK_THREAD_CPUTIME_ID) + CLOCK_THREAD_CPUTIME_ID, +#endif + }; + + for(clockid_t clock : clock_types) + { + struct timespec ts; + if(::clock_gettime(clock, &ts) == 0) + { + return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec); + } + } +#endif + + // Plain C++11 fallback + auto now = std::chrono::high_resolution_clock::now().time_since_epoch(); + return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count(); + } + +uint64_t OS::get_system_timestamp_ns() + { +#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME) + struct timespec ts; + if(::clock_gettime(CLOCK_REALTIME, &ts) == 0) + { + return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec); + } +#endif + + auto now = std::chrono::system_clock::now().time_since_epoch(); + return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count(); + } + +size_t OS::system_page_size() + { + const size_t default_page_size = 4096; + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + long p = ::sysconf(_SC_PAGESIZE); + if(p > 1) + return static_cast<size_t>(p); + else + return default_page_size; +#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK) + BOTAN_UNUSED(default_page_size); + SYSTEM_INFO sys_info; + ::GetSystemInfo(&sys_info); + return sys_info.dwPageSize; +#else + return default_page_size; +#endif + } + +size_t OS::get_memory_locking_limit() + { +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) && defined(RLIMIT_MEMLOCK) + /* + * If RLIMIT_MEMLOCK is not defined, likely the OS does not support + * unprivileged mlock calls. + * + * Linux defaults to only 64 KiB of mlockable memory per process + * (too small) but BSDs offer a small fraction of total RAM (more + * than we need). Bound the total mlock size to 512 KiB which is + * enough to run the entire test suite without spilling to non-mlock + * memory (and thus presumably also enough for many useful + * programs), but small enough that we should not cause problems + * even if many processes are mlocking on the same machine. + */ + const size_t user_req = read_env_variable_sz("BOTAN_MLOCK_POOL_SIZE", BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB); + + const size_t mlock_requested = std::min<size_t>(user_req, BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB); + + if(mlock_requested > 0) + { + struct ::rlimit limits; + + ::getrlimit(RLIMIT_MEMLOCK, &limits); + + if(limits.rlim_cur < limits.rlim_max) + { + limits.rlim_cur = limits.rlim_max; + ::setrlimit(RLIMIT_MEMLOCK, &limits); + ::getrlimit(RLIMIT_MEMLOCK, &limits); + } + + return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024); + } + +#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK) + SIZE_T working_min = 0, working_max = 0; + if(!::GetProcessWorkingSetSize(::GetCurrentProcess(), &working_min, &working_max)) + { + return 0; + } + + // According to Microsoft MSDN: + // The maximum number of pages that a process can lock is equal to the number of pages in its minimum working set minus a small overhead + // In the book "Windows Internals Part 2": the maximum lockable pages are minimum working set size - 8 pages + // But the information in the book seems to be inaccurate/outdated + // I've tested this on Windows 8.1 x64, Windows 10 x64 and Windows 7 x86 + // On all three OS the value is 11 instead of 8 + const size_t overhead = OS::system_page_size() * 11; + if(working_min > overhead) + { + const size_t lockable_bytes = working_min - overhead; + return std::min<size_t>(lockable_bytes, BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024); + } +#endif + + // Not supported on this platform + return 0; + } + +bool OS::read_env_variable(std::string& value_out, const std::string& name) + { + value_out = ""; + + if(running_in_privileged_state()) + return false; + +#if defined(BOTAN_TARGET_OS_HAS_WIN32) && defined(BOTAN_BUILD_COMPILER_IS_MSVC) + char val[128] = { 0 }; + size_t req_size = 0; + if(getenv_s(&req_size, val, sizeof(val), name.c_str()) == 0) + { + value_out = std::string(val, req_size); + return true; + } +#else + if(const char* val = std::getenv(name.c_str())) + { + value_out = val; + return true; + } +#endif + + return false; + } + +size_t OS::read_env_variable_sz(const std::string& name, size_t def) + { + std::string value; + if(read_env_variable(value, name)) + { + try + { + const size_t val = std::stoul(value, nullptr); + return val; + } + catch(std::exception&) { /* ignore it */ } + } + + return def; + } + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) + +namespace { + +int get_locked_fd() + { +#if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS) + // On Darwin, tagging anonymous pages allows vmmap to track these. + // Allowed from 240 to 255 for userland applications + static constexpr int default_locked_fd = 255; + int locked_fd = default_locked_fd; + + if(size_t locked_fdl = OS::read_env_variable_sz("BOTAN_LOCKED_FD", default_locked_fd)) + { + if(locked_fdl < 240 || locked_fdl > 255) + { + locked_fdl = default_locked_fd; + } + locked_fd = static_cast<int>(locked_fdl); + } + return VM_MAKE_TAG(locked_fd); +#else + return -1; +#endif + } + +} + +#endif + +std::vector<void*> OS::allocate_locked_pages(size_t count) + { + std::vector<void*> result; + +#if (defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)) || defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK) + + result.reserve(count); + + const size_t page_size = OS::system_page_size(); + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) + static const int locked_fd = get_locked_fd(); +#endif + + for(size_t i = 0; i != count; ++i) + { + void* ptr = nullptr; + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) + +#if !defined(MAP_ANONYMOUS) + #define MAP_ANONYMOUS MAP_ANON +#endif + +#if !defined(MAP_NOCORE) +#if defined(MAP_CONCEAL) + #define MAP_NOCORE MAP_CONCEAL +#else + #define MAP_NOCORE 0 +#endif +#endif + +#if !defined(PROT_MAX) + #define PROT_MAX(p) 0 +#endif + const int pflags = PROT_READ | PROT_WRITE; + + ptr = ::mmap(nullptr, 3*page_size, + pflags | PROT_MAX(pflags), + MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, + /*fd=*/locked_fd, /*offset=*/0); + + if(ptr == MAP_FAILED) + { + continue; + } + + // lock the data page + if(::mlock(static_cast<uint8_t*>(ptr) + page_size, page_size) != 0) + { + ::munmap(ptr, 3*page_size); + continue; + } + +#if defined(MADV_DONTDUMP) + // we ignore errors here, as DONTDUMP is just a bonus + ::madvise(static_cast<uint8_t*>(ptr) + page_size, page_size, MADV_DONTDUMP); +#endif + +#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK) + ptr = ::VirtualAlloc(nullptr, 3*page_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + + if(ptr == nullptr) + continue; + + if(::VirtualLock(static_cast<uint8_t*>(ptr) + page_size, page_size) == 0) + { + ::VirtualFree(ptr, 0, MEM_RELEASE); + continue; + } +#endif + + std::memset(ptr, 0, 3*page_size); // zero data page and both guard pages + + // Make guard page preceeding the data page + page_prohibit_access(static_cast<uint8_t*>(ptr)); + // Make guard page following the data page + page_prohibit_access(static_cast<uint8_t*>(ptr) + 2*page_size); + + result.push_back(static_cast<uint8_t*>(ptr) + page_size); + } +#else + BOTAN_UNUSED(count); +#endif + + return result; + } + +void OS::page_allow_access(void* page) + { +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + const size_t page_size = OS::system_page_size(); + ::mprotect(page, page_size, PROT_READ | PROT_WRITE); +#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK) + const size_t page_size = OS::system_page_size(); + DWORD old_perms = 0; + ::VirtualProtect(page, page_size, PAGE_READWRITE, &old_perms); + BOTAN_UNUSED(old_perms); +#else + BOTAN_UNUSED(page); +#endif + } + +void OS::page_prohibit_access(void* page) + { +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + const size_t page_size = OS::system_page_size(); + ::mprotect(page, page_size, PROT_NONE); +#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK) + const size_t page_size = OS::system_page_size(); + DWORD old_perms = 0; + ::VirtualProtect(page, page_size, PAGE_NOACCESS, &old_perms); + BOTAN_UNUSED(old_perms); +#else + BOTAN_UNUSED(page); +#endif + } + +void OS::free_locked_pages(const std::vector<void*>& pages) + { + const size_t page_size = OS::system_page_size(); + + for(size_t i = 0; i != pages.size(); ++i) + { + void* ptr = pages[i]; + + secure_scrub_memory(ptr, page_size); + + // ptr points to the data page, guard pages are before and after + page_allow_access(static_cast<uint8_t*>(ptr) - page_size); + page_allow_access(static_cast<uint8_t*>(ptr) + page_size); + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) + ::munlock(ptr, page_size); + ::munmap(static_cast<uint8_t*>(ptr) - page_size, 3*page_size); +#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK) + ::VirtualUnlock(ptr, page_size); + ::VirtualFree(static_cast<uint8_t*>(ptr) - page_size, 0, MEM_RELEASE); +#endif + } + } + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN) + +namespace { + +static ::sigjmp_buf g_sigill_jmp_buf; + +void botan_sigill_handler(int) + { + siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/1); + } + +} + +#endif + +int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn) + { + volatile int probe_result = -3; + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN) + struct sigaction old_sigaction; + struct sigaction sigaction; + + sigaction.sa_handler = botan_sigill_handler; + sigemptyset(&sigaction.sa_mask); + sigaction.sa_flags = 0; + + int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction); + + if(rc != 0) + throw System_Error("run_cpu_instruction_probe sigaction failed", errno); + + rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1); + + if(rc == 0) + { + // first call to sigsetjmp + probe_result = probe_fn(); + } + else if(rc == 1) + { + // non-local return from siglongjmp in signal handler: return error + probe_result = -1; + } + + // Restore old SIGILL handler, if any + rc = ::sigaction(SIGILL, &old_sigaction, nullptr); + if(rc != 0) + throw System_Error("run_cpu_instruction_probe sigaction restore failed", errno); + +#else + BOTAN_UNUSED(probe_fn); +#endif + + return probe_result; + } + +std::unique_ptr<OS::Echo_Suppression> OS::suppress_echo_on_terminal() + { +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + class POSIX_Echo_Suppression : public Echo_Suppression + { + public: + POSIX_Echo_Suppression() + { + m_stdin_fd = fileno(stdin); + if(::tcgetattr(m_stdin_fd, &m_old_termios) != 0) + throw System_Error("Getting terminal status failed", errno); + + struct termios noecho_flags = m_old_termios; + noecho_flags.c_lflag &= ~ECHO; + noecho_flags.c_lflag |= ECHONL; + + if(::tcsetattr(m_stdin_fd, TCSANOW, &noecho_flags) != 0) + throw System_Error("Clearing terminal echo bit failed", errno); + } + + void reenable_echo() override + { + if(m_stdin_fd > 0) + { + if(::tcsetattr(m_stdin_fd, TCSANOW, &m_old_termios) != 0) + throw System_Error("Restoring terminal echo bit failed", errno); + m_stdin_fd = -1; + } + } + + ~POSIX_Echo_Suppression() + { + try + { + reenable_echo(); + } + catch(...) + { + } + } + + private: + int m_stdin_fd; + struct termios m_old_termios; + }; + + return std::unique_ptr<Echo_Suppression>(new POSIX_Echo_Suppression); + +#elif defined(BOTAN_TARGET_OS_HAS_WIN32) + + class Win32_Echo_Suppression : public Echo_Suppression + { + public: + Win32_Echo_Suppression() + { + m_input_handle = ::GetStdHandle(STD_INPUT_HANDLE); + if(::GetConsoleMode(m_input_handle, &m_console_state) == 0) + throw System_Error("Getting console mode failed", ::GetLastError()); + + DWORD new_mode = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; + if(::SetConsoleMode(m_input_handle, new_mode) == 0) + throw System_Error("Setting console mode failed", ::GetLastError()); + } + + void reenable_echo() override + { + if(m_input_handle != INVALID_HANDLE_VALUE) + { + if(::SetConsoleMode(m_input_handle, m_console_state) == 0) + throw System_Error("Setting console mode failed", ::GetLastError()); + m_input_handle = INVALID_HANDLE_VALUE; + } + } + + ~Win32_Echo_Suppression() + { + try + { + reenable_echo(); + } + catch(...) + { + } + } + + private: + HANDLE m_input_handle; + DWORD m_console_state; + }; + + return std::unique_ptr<Echo_Suppression>(new Win32_Echo_Suppression); + +#else + + // Not supported on this platform, return null + return std::unique_ptr<Echo_Suppression>(); +#endif + } + +} diff --git a/comm/third_party/botan/src/lib/utils/os_utils.h b/comm/third_party/botan/src/lib/utils/os_utils.h new file mode 100644 index 0000000000..d31dcb3baf --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/os_utils.h @@ -0,0 +1,200 @@ +/* +* OS specific utility functions +* (C) 2015,2016,2017,2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_OS_UTILS_H_ +#define BOTAN_OS_UTILS_H_ + +#include <botan/types.h> +#include <functional> +#include <string> +#include <vector> + +namespace Botan { + +namespace OS { + +/* +* This header is internal (not installed) and these functions are not +* intended to be called by applications. However they are given public +* visibility (using BOTAN_TEST_API macro) for the tests. This also probably +* allows them to be overridden by the application on ELF systems, but +* this hasn't been tested. +*/ + +/** +* @return process ID assigned by the operating system. +* On Unix and Windows systems, this always returns a result +* On IncludeOS it returns 0 since there is no process ID to speak of +* in a unikernel. +*/ +uint32_t BOTAN_TEST_API get_process_id(); + +/** +* Test if we are currently running with elevated permissions +* eg setuid, setgid, or with POSIX caps set. +*/ +bool running_in_privileged_state(); + +/** +* @return CPU processor clock, if available +* +* On Windows, calls QueryPerformanceCounter. +* +* Under GCC or Clang on supported platforms the hardware cycle counter is queried. +* Currently supported processors are x86, PPC, Alpha, SPARC, IA-64, S/390x, and HP-PA. +* If no CPU cycle counter is available on this system, returns zero. +*/ +uint64_t BOTAN_TEST_API get_cpu_cycle_counter(); + +size_t BOTAN_TEST_API get_cpu_total(); +size_t BOTAN_TEST_API get_cpu_available(); + +/** +* Return the ELF auxiliary vector cooresponding to the given ID. +* This only makes sense on Unix-like systems and is currently +* only supported on Linux, Android, and FreeBSD. +* +* Returns zero if not supported on the current system or if +* the id provided is not known. +*/ +unsigned long get_auxval(unsigned long id); + +/* +* @return best resolution timestamp available +* +* The epoch and update rate of this clock is arbitrary and depending +* on the hardware it may not tick at a constant rate. +* +* Uses hardware cycle counter, if available. +* On POSIX platforms clock_gettime is used with a monotonic timer +* As a final fallback std::chrono::high_resolution_clock is used. +*/ +uint64_t BOTAN_TEST_API get_high_resolution_clock(); + +/** +* @return system clock (reflecting wall clock) with best resolution +* available, normalized to nanoseconds resolution. +*/ +uint64_t BOTAN_TEST_API get_system_timestamp_ns(); + +/** +* @return maximum amount of memory (in bytes) Botan could/should +* hyptothetically allocate for the memory poool. Reads environment +* variable "BOTAN_MLOCK_POOL_SIZE", set to "0" to disable pool. +*/ +size_t get_memory_locking_limit(); + +/** +* Return the size of a memory page, if that can be derived on the +* current system. Otherwise returns some default value (eg 4096) +*/ +size_t system_page_size(); + +/** +* Read the value of an environment variable, setting it to value_out if it +* exists. Returns false and sets value_out to empty string if no such variable +* is set. If the process seems to be running in a privileged state (such as +* setuid) then always returns false and does not examine the environment. +*/ +bool read_env_variable(std::string& value_out, const std::string& var_name); + +/** +* Read the value of an environment variable and convert it to an +* integer. If not set or conversion fails, returns the default value. +* +* If the process seems to be running in a privileged state (such as setuid) +* then always returns nullptr, similiar to glibc's secure_getenv. +*/ +size_t read_env_variable_sz(const std::string& var_name, size_t def_value = 0); + +/** +* Request count pages of RAM which are locked into memory using mlock, +* VirtualLock, or some similar OS specific API. Free it with free_locked_pages. +* +* Returns an empty list on failure. This function is allowed to return fewer +* than count pages. +* +* The contents of the allocated pages are undefined. +* +* Each page is preceded by and followed by a page which is marked +* as noaccess, such that accessing it will cause a crash. This turns +* out of bound reads/writes into crash events. +* +* @param count requested number of locked pages +*/ +std::vector<void*> allocate_locked_pages(size_t count); + +/** +* Free memory allocated by allocate_locked_pages +* @param pages a list of pages returned by allocate_locked_pages +*/ +void free_locked_pages(const std::vector<void*>& pages); + +/** +* Set the MMU to prohibit access to this page +*/ +void page_prohibit_access(void* page); + +/** +* Set the MMU to allow R/W access to this page +*/ +void page_allow_access(void* page); + + +/** +* Run a probe instruction to test for support for a CPU instruction. +* Runs in system-specific env that catches illegal instructions; this +* function always fails if the OS doesn't provide this. +* Returns value of probe_fn, if it could run. +* If error occurs, returns negative number. +* This allows probe_fn to indicate errors of its own, if it wants. +* For example the instruction might not only be only available on some +* CPUs, but also buggy on some subset of these - the probe function +* can test to make sure the instruction works properly before +* indicating that the instruction is available. +* +* @warning on Unix systems uses signal handling in a way that is not +* thread safe. It should only be called in a single-threaded context +* (ie, at static init time). +* +* If probe_fn throws an exception the result is undefined. +* +* Return codes: +* -1 illegal instruction detected +*/ +int BOTAN_TEST_API run_cpu_instruction_probe(std::function<int ()> probe_fn); + +/** +* Represents a terminal state +*/ +class BOTAN_UNSTABLE_API Echo_Suppression + { + public: + /** + * Reenable echo on this terminal. Can be safely called + * multiple times. May throw if an error occurs. + */ + virtual void reenable_echo() = 0; + + /** + * Implicitly calls reenable_echo, but swallows/ignored all + * errors which would leave the terminal in an invalid state. + */ + virtual ~Echo_Suppression() = default; + }; + +/** +* Suppress echo on the terminal +* Returns null if this operation is not supported on the current system. +*/ +std::unique_ptr<Echo_Suppression> BOTAN_UNSTABLE_API suppress_echo_on_terminal(); + +} + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/parsing.cpp b/comm/third_party/botan/src/lib/utils/parsing.cpp new file mode 100644 index 0000000000..ff3d4ac0a2 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/parsing.cpp @@ -0,0 +1,458 @@ +/* +* Various string utils and parsing functions +* (C) 1999-2007,2013,2014,2015,2018 Jack Lloyd +* (C) 2015 Simon Warta (Kullo GmbH) +* (C) 2017 RenĂ© Korthaus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/parsing.h> +#include <botan/exceptn.h> +#include <botan/charset.h> +#include <botan/loadstor.h> +#include <algorithm> +#include <cctype> +#include <limits> +#include <set> + +#if defined(BOTAN_HAS_ASN1) + #include <botan/asn1_obj.h> +#endif + +namespace Botan { + +uint16_t to_uint16(const std::string& str) + { + const uint32_t x = to_u32bit(str); + + if(x >> 16) + throw Invalid_Argument("Integer value exceeds 16 bit range"); + + return static_cast<uint16_t>(x); + } + +uint32_t to_u32bit(const std::string& str) + { + // std::stoul is not strict enough. Ensure that str is digit only [0-9]* + for(const char chr : str) + { + if(chr < '0' || chr > '9') + { + std::string chrAsString(1, chr); + throw Invalid_Argument("String contains non-digit char: " + chrAsString); + } + } + + const unsigned long int x = std::stoul(str); + + if(sizeof(unsigned long int) > 4) + { + // x might be uint64 + if (x > std::numeric_limits<uint32_t>::max()) + { + throw Invalid_Argument("Integer value of " + str + " exceeds 32 bit range"); + } + } + + return static_cast<uint32_t>(x); + } + +/* +* Convert a string into a time duration +*/ +uint32_t timespec_to_u32bit(const std::string& timespec) + { + if(timespec.empty()) + return 0; + + const char suffix = timespec[timespec.size()-1]; + std::string value = timespec.substr(0, timespec.size()-1); + + uint32_t scale = 1; + + if(Charset::is_digit(suffix)) + value += suffix; + else if(suffix == 's') + scale = 1; + else if(suffix == 'm') + scale = 60; + else if(suffix == 'h') + scale = 60 * 60; + else if(suffix == 'd') + scale = 24 * 60 * 60; + else if(suffix == 'y') + scale = 365 * 24 * 60 * 60; + else + throw Decoding_Error("timespec_to_u32bit: Bad input " + timespec); + + return scale * to_u32bit(value); + } + +/* +* Parse a SCAN-style algorithm name +*/ +std::vector<std::string> parse_algorithm_name(const std::string& namex) + { + if(namex.find('(') == std::string::npos && + namex.find(')') == std::string::npos) + return std::vector<std::string>(1, namex); + + std::string name = namex, substring; + std::vector<std::string> elems; + size_t level = 0; + + elems.push_back(name.substr(0, name.find('('))); + name = name.substr(name.find('(')); + + for(auto i = name.begin(); i != name.end(); ++i) + { + char c = *i; + + if(c == '(') + ++level; + if(c == ')') + { + if(level == 1 && i == name.end() - 1) + { + if(elems.size() == 1) + elems.push_back(substring.substr(1)); + else + elems.push_back(substring); + return elems; + } + + if(level == 0 || (level == 1 && i != name.end() - 1)) + throw Invalid_Algorithm_Name(namex); + --level; + } + + if(c == ',' && level == 1) + { + if(elems.size() == 1) + elems.push_back(substring.substr(1)); + else + elems.push_back(substring); + substring.clear(); + } + else + substring += c; + } + + if(!substring.empty()) + throw Invalid_Algorithm_Name(namex); + + return elems; + } + +std::vector<std::string> split_on(const std::string& str, char delim) + { + return split_on_pred(str, [delim](char c) { return c == delim; }); + } + +std::vector<std::string> split_on_pred(const std::string& str, + std::function<bool (char)> pred) + { + std::vector<std::string> elems; + if(str.empty()) return elems; + + std::string substr; + for(auto i = str.begin(); i != str.end(); ++i) + { + if(pred(*i)) + { + if(!substr.empty()) + elems.push_back(substr); + substr.clear(); + } + else + substr += *i; + } + + if(substr.empty()) + throw Invalid_Argument("Unable to split string: " + str); + elems.push_back(substr); + + return elems; + } + +/* +* Join a string +*/ +std::string string_join(const std::vector<std::string>& strs, char delim) + { + std::string out = ""; + + for(size_t i = 0; i != strs.size(); ++i) + { + if(i != 0) + out += delim; + out += strs[i]; + } + + return out; + } + +/* +* Parse an ASN.1 OID string +*/ +std::vector<uint32_t> parse_asn1_oid(const std::string& oid) + { +#if defined(BOTAN_HAS_ASN1) + return OID(oid).get_components(); +#else + BOTAN_UNUSED(oid); + throw Not_Implemented("ASN1 support not available"); +#endif + } + +/* +* X.500 String Comparison +*/ +bool x500_name_cmp(const std::string& name1, const std::string& name2) + { + auto p1 = name1.begin(); + auto p2 = name2.begin(); + + while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1; + while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2; + + while(p1 != name1.end() && p2 != name2.end()) + { + if(Charset::is_space(*p1)) + { + if(!Charset::is_space(*p2)) + return false; + + while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1; + while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2; + + if(p1 == name1.end() && p2 == name2.end()) + return true; + if(p1 == name1.end() || p2 == name2.end()) + return false; + } + + if(!Charset::caseless_cmp(*p1, *p2)) + return false; + ++p1; + ++p2; + } + + while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1; + while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2; + + if((p1 != name1.end()) || (p2 != name2.end())) + return false; + return true; + } + +/* +* Convert a decimal-dotted string to binary IP +*/ +uint32_t string_to_ipv4(const std::string& str) + { + std::vector<std::string> parts = split_on(str, '.'); + + if(parts.size() != 4) + throw Decoding_Error("Invalid IP string " + str); + + uint32_t ip = 0; + + for(auto part = parts.begin(); part != parts.end(); ++part) + { + uint32_t octet = to_u32bit(*part); + + if(octet > 255) + throw Decoding_Error("Invalid IP string " + str); + + ip = (ip << 8) | (octet & 0xFF); + } + + return ip; + } + +/* +* Convert an IP address to decimal-dotted string +*/ +std::string ipv4_to_string(uint32_t ip) + { + std::string str; + + for(size_t i = 0; i != sizeof(ip); ++i) + { + if(i) + str += "."; + str += std::to_string(get_byte(i, ip)); + } + + return str; + } + +std::string erase_chars(const std::string& str, const std::set<char>& chars) + { + std::string out; + + for(auto c: str) + if(chars.count(c) == 0) + out += c; + + return out; + } + +std::string replace_chars(const std::string& str, + const std::set<char>& chars, + char to_char) + { + std::string out = str; + + for(size_t i = 0; i != out.size(); ++i) + if(chars.count(out[i])) + out[i] = to_char; + + return out; + } + +std::string replace_char(const std::string& str, char from_char, char to_char) + { + std::string out = str; + + for(size_t i = 0; i != out.size(); ++i) + if(out[i] == from_char) + out[i] = to_char; + + return out; + } + +std::string tolower_string(const std::string& in) + { + std::string s = in; + for(size_t i = 0; i != s.size(); ++i) + { + const int cu = static_cast<unsigned char>(s[i]); + if(std::isalpha(cu)) + s[i] = static_cast<char>(std::tolower(cu)); + } + return s; + } + +bool host_wildcard_match(const std::string& issued_, const std::string& host_) + { + const std::string issued = tolower_string(issued_); + const std::string host = tolower_string(host_); + + if(host.empty() || issued.empty()) + return false; + + /* + If there are embedded nulls in your issued name + Well I feel bad for you son + */ + if(std::count(issued.begin(), issued.end(), char(0)) > 0) + return false; + + // If more than one wildcard, then issued name is invalid + const size_t stars = std::count(issued.begin(), issued.end(), '*'); + if(stars > 1) + return false; + + // '*' is not a valid character in DNS names so should not appear on the host side + if(std::count(host.begin(), host.end(), '*') != 0) + return false; + + // Similarly a DNS name can't end in . + if(host[host.size() - 1] == '.') + return false; + + // And a host can't have an empty name component, so reject that + if(host.find("..") != std::string::npos) + return false; + + // Exact match: accept + if(issued == host) + { + return true; + } + + /* + Otherwise it might be a wildcard + + If the issued size is strictly longer than the hostname size it + couldn't possibly be a match, even if the issued value is a + wildcard. The only exception is when the wildcard ends up empty + (eg www.example.com matches www*.example.com) + */ + if(issued.size() > host.size() + 1) + { + return false; + } + + // If no * at all then not a wildcard, and so not a match + if(stars != 1) + { + return false; + } + + /* + Now walk through the issued string, making sure every character + matches. When we come to the (singular) '*', jump forward in the + hostname by the corresponding amount. We know exactly how much + space the wildcard takes because it must be exactly `len(host) - + len(issued) + 1 chars`. + + We also verify that the '*' comes in the leftmost component, and + doesn't skip over any '.' in the hostname. + */ + size_t dots_seen = 0; + size_t host_idx = 0; + + for(size_t i = 0; i != issued.size(); ++i) + { + dots_seen += (issued[i] == '.'); + + if(issued[i] == '*') + { + // Fail: wildcard can only come in leftmost component + if(dots_seen > 0) + { + return false; + } + + /* + Since there is only one * we know the tail of the issued and + hostname must be an exact match. In this case advance host_idx + to match. + */ + const size_t advance = (host.size() - issued.size() + 1); + + if(host_idx + advance > host.size()) // shouldn't happen + return false; + + // Can't be any intervening .s that we would have skipped + if(std::count(host.begin() + host_idx, + host.begin() + host_idx + advance, '.') != 0) + return false; + + host_idx += advance; + } + else + { + if(issued[i] != host[host_idx]) + { + return false; + } + + host_idx += 1; + } + } + + // Wildcard issued name must have at least 3 components + if(dots_seen < 2) + { + return false; + } + + return true; + } + +} diff --git a/comm/third_party/botan/src/lib/utils/parsing.h b/comm/third_party/botan/src/lib/utils/parsing.h new file mode 100644 index 0000000000..d2c0b5f8c8 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/parsing.h @@ -0,0 +1,181 @@ +/* +* Various string utils and parsing functions +* (C) 1999-2007,2013 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PARSING_UTILS_H_ +#define BOTAN_PARSING_UTILS_H_ + +#include <botan/types.h> +#include <string> +#include <vector> +#include <set> + +#include <istream> +#include <functional> +#include <map> + +BOTAN_FUTURE_INTERNAL_HEADER(parsing.h) + +namespace Botan { + +/** +* Parse a SCAN-style algorithm name +* @param scan_name the name +* @return the name components +*/ +BOTAN_PUBLIC_API(2,0) std::vector<std::string> +parse_algorithm_name(const std::string& scan_name); + +/** +* Split a string +* @param str the input string +* @param delim the delimitor +* @return string split by delim +*/ +BOTAN_PUBLIC_API(2,0) std::vector<std::string> split_on( + const std::string& str, char delim); + +/** +* Split a string on a character predicate +* @param str the input string +* @param pred the predicate +* +* This function will likely be removed in a future release +*/ +BOTAN_PUBLIC_API(2,0) std::vector<std::string> +split_on_pred(const std::string& str, + std::function<bool (char)> pred); + +/** +* Erase characters from a string +*/ +BOTAN_PUBLIC_API(2,0) +BOTAN_DEPRECATED("Unused") +std::string erase_chars(const std::string& str, const std::set<char>& chars); + +/** +* Replace a character in a string +* @param str the input string +* @param from_char the character to replace +* @param to_char the character to replace it with +* @return str with all instances of from_char replaced by to_char +*/ +BOTAN_PUBLIC_API(2,0) +BOTAN_DEPRECATED("Unused") +std::string replace_char(const std::string& str, + char from_char, + char to_char); + +/** +* Replace a character in a string +* @param str the input string +* @param from_chars the characters to replace +* @param to_char the character to replace it with +* @return str with all instances of from_chars replaced by to_char +*/ +BOTAN_PUBLIC_API(2,0) +BOTAN_DEPRECATED("Unused") +std::string replace_chars(const std::string& str, + const std::set<char>& from_chars, + char to_char); + +/** +* Join a string +* @param strs strings to join +* @param delim the delimitor +* @return string joined by delim +*/ +BOTAN_PUBLIC_API(2,0) +std::string string_join(const std::vector<std::string>& strs, + char delim); + +/** +* Parse an ASN.1 OID +* @param oid the OID in string form +* @return OID components +*/ +BOTAN_PUBLIC_API(2,0) std::vector<uint32_t> +BOTAN_DEPRECATED("Use OID::from_string(oid).get_components()") parse_asn1_oid(const std::string& oid); + +/** +* Compare two names using the X.509 comparison algorithm +* @param name1 the first name +* @param name2 the second name +* @return true if name1 is the same as name2 by the X.509 comparison rules +*/ +BOTAN_PUBLIC_API(2,0) +bool x500_name_cmp(const std::string& name1, + const std::string& name2); + +/** +* Convert a string to a number +* @param str the string to convert +* @return number value of the string +*/ +BOTAN_PUBLIC_API(2,0) uint32_t to_u32bit(const std::string& str); + +/** +* Convert a string to a number +* @param str the string to convert +* @return number value of the string +*/ +BOTAN_PUBLIC_API(2,3) uint16_t to_uint16(const std::string& str); + +/** +* Convert a time specification to a number +* @param timespec the time specification +* @return number of seconds represented by timespec +*/ +BOTAN_PUBLIC_API(2,0) uint32_t BOTAN_DEPRECATED("Not used anymore") +timespec_to_u32bit(const std::string& timespec); + +/** +* Convert a string representation of an IPv4 address to a number +* @param ip_str the string representation +* @return integer IPv4 address +*/ +BOTAN_PUBLIC_API(2,0) uint32_t string_to_ipv4(const std::string& ip_str); + +/** +* Convert an IPv4 address to a string +* @param ip_addr the IPv4 address to convert +* @return string representation of the IPv4 address +*/ +BOTAN_PUBLIC_API(2,0) std::string ipv4_to_string(uint32_t ip_addr); + +std::map<std::string, std::string> BOTAN_PUBLIC_API(2,0) read_cfg(std::istream& is); + +/** +* Accepts key value pairs deliminated by commas: +* +* "" (returns empty map) +* "K=V" (returns map {'K': 'V'}) +* "K1=V1,K2=V2" +* "K1=V1,K2=V2,K3=V3" +* "K1=V1,K2=V2,K3=a_value\,with\,commas_and_\=equals" +* +* Values may be empty, keys must be non-empty and unique. Duplicate +* keys cause an exception. +* +* Within both key and value, comma and equals can be escaped with +* backslash. Backslash can also be escaped. +*/ +std::map<std::string, std::string> BOTAN_PUBLIC_API(2,8) read_kv(const std::string& kv); + +std::string BOTAN_PUBLIC_API(2,0) clean_ws(const std::string& s); + +std::string tolower_string(const std::string& s); + +/** +* Check if the given hostname is a match for the specified wildcard +*/ +bool BOTAN_PUBLIC_API(2,0) host_wildcard_match(const std::string& wildcard, + const std::string& host); + + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/poly_dbl/info.txt b/comm/third_party/botan/src/lib/utils/poly_dbl/info.txt new file mode 100644 index 0000000000..5aae5b44f4 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/poly_dbl/info.txt @@ -0,0 +1,7 @@ +<defines> +POLY_DBL -> 20170927 +</defines> + +<header:internal> +poly_dbl.h +</header:internal> diff --git a/comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.cpp b/comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.cpp new file mode 100644 index 0000000000..8c728a1480 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.cpp @@ -0,0 +1,115 @@ +/* +* (C) 2017,2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/poly_dbl.h> +#include <botan/loadstor.h> +#include <botan/exceptn.h> + +namespace Botan { + +namespace { + +/* +* The minimum weight irreducible binary polynomial of size n +* +* See http://www.hpl.hp.com/techreports/98/HPL-98-135.pdf +*/ +enum class MinWeightPolynomial : uint64_t { + P64 = 0x1B, + P128 = 0x87, + P192 = 0x87, + P256 = 0x425, + P512 = 0x125, + P1024 = 0x80043, +}; + +template<size_t LIMBS, MinWeightPolynomial P> +void poly_double(uint8_t out[], const uint8_t in[]) + { + uint64_t W[LIMBS]; + load_be(W, in, LIMBS); + + const uint64_t POLY = static_cast<uint64_t>(P); + + const uint64_t carry = POLY * (W[0] >> 63); + + BOTAN_IF_CONSTEXPR(LIMBS > 0) + { + for(size_t i = 0; i != LIMBS - 1; ++i) + W[i] = (W[i] << 1) ^ (W[i+1] >> 63); + } + + W[LIMBS-1] = (W[LIMBS-1] << 1) ^ carry; + + copy_out_be(out, LIMBS*8, W); + } + +template<size_t LIMBS, MinWeightPolynomial P> +void poly_double_le(uint8_t out[], const uint8_t in[]) + { + uint64_t W[LIMBS]; + load_le(W, in, LIMBS); + + const uint64_t POLY = static_cast<uint64_t>(P); + + const uint64_t carry = POLY * (W[LIMBS-1] >> 63); + + BOTAN_IF_CONSTEXPR(LIMBS > 0) + { + for(size_t i = 0; i != LIMBS - 1; ++i) + W[LIMBS-1-i] = (W[LIMBS-1-i] << 1) ^ (W[LIMBS-2-i] >> 63); + } + + W[0] = (W[0] << 1) ^ carry; + + copy_out_le(out, LIMBS*8, W); + } + +} + +void poly_double_n(uint8_t out[], const uint8_t in[], size_t n) + { + switch(n) + { + case 8: + return poly_double<1, MinWeightPolynomial::P64>(out, in); + case 16: + return poly_double<2, MinWeightPolynomial::P128>(out, in); + case 24: + return poly_double<3, MinWeightPolynomial::P192>(out, in); + case 32: + return poly_double<4, MinWeightPolynomial::P256>(out, in); + case 64: + return poly_double<8, MinWeightPolynomial::P512>(out, in); + case 128: + return poly_double<16, MinWeightPolynomial::P1024>(out, in); + default: + throw Invalid_Argument("Unsupported size for poly_double_n"); + } + } + +void poly_double_n_le(uint8_t out[], const uint8_t in[], size_t n) + { + switch(n) + { + case 8: + return poly_double_le<1, MinWeightPolynomial::P64>(out, in); + case 16: + return poly_double_le<2, MinWeightPolynomial::P128>(out, in); + case 24: + return poly_double_le<3, MinWeightPolynomial::P192>(out, in); + case 32: + return poly_double_le<4, MinWeightPolynomial::P256>(out, in); + case 64: + return poly_double_le<8, MinWeightPolynomial::P512>(out, in); + case 128: + return poly_double_le<16, MinWeightPolynomial::P1024>(out, in); + default: + throw Invalid_Argument("Unsupported size for poly_double_n_le"); + } + } + +} diff --git a/comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.h b/comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.h new file mode 100644 index 0000000000..7d9f523319 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.h @@ -0,0 +1,39 @@ +/* +* (C) 2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_POLY_DBL_H_ +#define BOTAN_POLY_DBL_H_ + +#include <botan/types.h> + +namespace Botan { + +/** +* Polynomial doubling in GF(2^n) +*/ +void BOTAN_TEST_API poly_double_n(uint8_t out[], const uint8_t in[], size_t n); + +/** +* Returns true iff poly_double_n is implemented for this size. +*/ +inline bool poly_double_supported_size(size_t n) + { + return (n == 8 || n == 16 || n == 24 || n == 32 || n == 64 || n == 128); + } + +inline void poly_double_n(uint8_t buf[], size_t n) + { + return poly_double_n(buf, buf, n); + } + +/* +* Little endian convention - used for XTS +*/ +void BOTAN_TEST_API poly_double_n_le(uint8_t out[], const uint8_t in[], size_t n); + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/prefetch.h b/comm/third_party/botan/src/lib/utils/prefetch.h new file mode 100644 index 0000000000..92c41e5738 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/prefetch.h @@ -0,0 +1,39 @@ +/* +* Prefetching Operations +* (C) 2009 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PREFETCH_H_ +#define BOTAN_PREFETCH_H_ + +#include <botan/cpuid.h> + +namespace Botan { + +template<typename T> +inline void prefetch_readonly(const T* addr, size_t length) + { +#if defined(__GNUG__) + const size_t Ts_per_cache_line = CPUID::cache_line_size() / sizeof(T); + + for(size_t i = 0; i <= length; i += Ts_per_cache_line) + __builtin_prefetch(addr + i, 0); +#endif + } + +template<typename T> +inline void prefetch_readwrite(const T* addr, size_t length) + { +#if defined(__GNUG__) + const size_t Ts_per_cache_line = CPUID::cache_line_size() / sizeof(T); + + for(size_t i = 0; i <= length; i += Ts_per_cache_line) + __builtin_prefetch(addr + i, 1); +#endif + } + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/read_cfg.cpp b/comm/third_party/botan/src/lib/utils/read_cfg.cpp new file mode 100644 index 0000000000..02f2e0ac81 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/read_cfg.cpp @@ -0,0 +1,63 @@ +/* +* Simple config/test file reader +* (C) 2013,2014,2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/parsing.h> +#include <botan/exceptn.h> + +namespace Botan { + +std::string clean_ws(const std::string& s) + { + const char* ws = " \t\n"; + auto start = s.find_first_not_of(ws); + auto end = s.find_last_not_of(ws); + + if(start == std::string::npos) + return ""; + + if(end == std::string::npos) + return s.substr(start, end); + else + return s.substr(start, start + end + 1); + } + +std::map<std::string, std::string> read_cfg(std::istream& is) + { + std::map<std::string, std::string> kv; + size_t line = 0; + + while(is.good()) + { + std::string s; + + std::getline(is, s); + + ++line; + + if(s.empty() || s[0] == '#') + continue; + + s = clean_ws(s.substr(0, s.find('#'))); + + if(s.empty()) + continue; + + auto eq = s.find("="); + + if(eq == std::string::npos || eq == 0 || eq == s.size() - 1) + throw Decoding_Error("Bad read_cfg input '" + s + "' on line " + std::to_string(line)); + + const std::string key = clean_ws(s.substr(0, eq)); + const std::string val = clean_ws(s.substr(eq + 1, std::string::npos)); + + kv[key] = val; + } + + return kv; + } + +} diff --git a/comm/third_party/botan/src/lib/utils/read_kv.cpp b/comm/third_party/botan/src/lib/utils/read_kv.cpp new file mode 100644 index 0000000000..cdc84c6229 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/read_kv.cpp @@ -0,0 +1,85 @@ +/* +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/parsing.h> +#include <botan/exceptn.h> + +namespace Botan { + +std::map<std::string, std::string> read_kv(const std::string& kv) + { + std::map<std::string, std::string> m; + if(kv == "") + return m; + + std::vector<std::string> parts; + + try + { + parts = split_on(kv, ','); + } + catch(std::exception&) + { + throw Invalid_Argument("Bad KV spec"); + } + + bool escaped = false; + bool reading_key = true; + std::string cur_key; + std::string cur_val; + + for(char c : kv) + { + if(c == '\\' && !escaped) + { + escaped = true; + } + else if(c == ',' && !escaped) + { + if(cur_key.empty()) + throw Invalid_Argument("Bad KV spec empty key"); + + if(m.find(cur_key) != m.end()) + throw Invalid_Argument("Bad KV spec duplicated key"); + m[cur_key] = cur_val; + cur_key = ""; + cur_val = ""; + reading_key = true; + } + else if(c == '=' && !escaped) + { + if(reading_key == false) + throw Invalid_Argument("Bad KV spec unexpected equals sign"); + reading_key = false; + } + else + { + if(reading_key) + cur_key += c; + else + cur_val += c; + + if(escaped) + escaped = false; + } + } + + if(!cur_key.empty()) + { + if(reading_key == false) + { + if(m.find(cur_key) != m.end()) + throw Invalid_Argument("Bad KV spec duplicated key"); + m[cur_key] = cur_val; + } + else + throw Invalid_Argument("Bad KV spec incomplete string"); + } + + return m; + } + +} diff --git a/comm/third_party/botan/src/lib/utils/rotate.h b/comm/third_party/botan/src/lib/utils/rotate.h new file mode 100644 index 0000000000..8599a2695f --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/rotate.h @@ -0,0 +1,112 @@ +/* +* Word Rotation Operations +* (C) 1999-2008,2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_WORD_ROTATE_H_ +#define BOTAN_WORD_ROTATE_H_ + +#include <botan/types.h> + +BOTAN_FUTURE_INTERNAL_HEADER(rotate.h) + +namespace Botan { + +/** +* Bit rotation left by a compile-time constant amount +* @param input the input word +* @return input rotated left by ROT bits +*/ +template<size_t ROT, typename T> +inline constexpr T rotl(T input) + { + static_assert(ROT > 0 && ROT < 8*sizeof(T), "Invalid rotation constant"); + return static_cast<T>((input << ROT) | (input >> (8*sizeof(T) - ROT))); + } + +/** +* Bit rotation right by a compile-time constant amount +* @param input the input word +* @return input rotated right by ROT bits +*/ +template<size_t ROT, typename T> +inline constexpr T rotr(T input) + { + static_assert(ROT > 0 && ROT < 8*sizeof(T), "Invalid rotation constant"); + return static_cast<T>((input >> ROT) | (input << (8*sizeof(T) - ROT))); + } + +/** +* Bit rotation left, variable rotation amount +* @param input the input word +* @param rot the number of bits to rotate, must be between 0 and sizeof(T)*8-1 +* @return input rotated left by rot bits +*/ +template<typename T> +inline T rotl_var(T input, size_t rot) + { + return rot ? static_cast<T>((input << rot) | (input >> (sizeof(T)*8 - rot))) : input; + } + +/** +* Bit rotation right, variable rotation amount +* @param input the input word +* @param rot the number of bits to rotate, must be between 0 and sizeof(T)*8-1 +* @return input rotated right by rot bits +*/ +template<typename T> +inline T rotr_var(T input, size_t rot) + { + return rot ? static_cast<T>((input >> rot) | (input << (sizeof(T)*8 - rot))) : input; + } + +#if defined(BOTAN_USE_GCC_INLINE_ASM) + +#if defined(BOTAN_TARGET_ARCH_IS_X86_64) || defined(BOTAN_TARGET_ARCH_IS_X86_32) + +template<> +inline uint32_t rotl_var(uint32_t input, size_t rot) + { + asm("roll %1,%0" + : "+r" (input) + : "c" (static_cast<uint8_t>(rot)) + : "cc"); + return input; + } + +template<> +inline uint32_t rotr_var(uint32_t input, size_t rot) + { + asm("rorl %1,%0" + : "+r" (input) + : "c" (static_cast<uint8_t>(rot)) + : "cc"); + return input; + } + +#endif + +#endif + + +template<typename T> +BOTAN_DEPRECATED("Use rotl<N> or rotl_var") +inline T rotate_left(T input, size_t rot) + { + // rotl_var does not reduce + return rotl_var(input, rot % (8 * sizeof(T))); + } + +template<typename T> +BOTAN_DEPRECATED("Use rotr<N> or rotr_var") +inline T rotate_right(T input, size_t rot) + { + // rotr_var does not reduce + return rotr_var(input, rot % (8 * sizeof(T))); + } + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/rounding.h b/comm/third_party/botan/src/lib/utils/rounding.h new file mode 100644 index 0000000000..ae0aedf07c --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/rounding.h @@ -0,0 +1,56 @@ +/* +* Integer Rounding Functions +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_ROUNDING_H_ +#define BOTAN_ROUNDING_H_ + +#include <botan/types.h> + +namespace Botan { + +/** +* Round up +* @param n a non-negative integer +* @param align_to the alignment boundary +* @return n rounded up to a multiple of align_to +*/ +inline size_t round_up(size_t n, size_t align_to) + { + BOTAN_ARG_CHECK(align_to != 0, "align_to must not be 0"); + + if(n % align_to) + n += align_to - (n % align_to); + return n; + } + +/** +* Round down +* @param n an integer +* @param align_to the alignment boundary +* @return n rounded down to a multiple of align_to +*/ +template<typename T> +inline constexpr T round_down(T n, T align_to) + { + return (align_to == 0) ? n : (n - (n % align_to)); + } + +/** +* Clamp +*/ +inline size_t clamp(size_t n, size_t lower_bound, size_t upper_bound) + { + if(n < lower_bound) + return lower_bound; + if(n > upper_bound) + return upper_bound; + return n; + } + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/safeint.h b/comm/third_party/botan/src/lib/utils/safeint.h new file mode 100644 index 0000000000..5c9ea49553 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/safeint.h @@ -0,0 +1,41 @@ +/* +* Safe(r) Integer Handling +* (C) 2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_UTILS_SAFE_INT_H_ +#define BOTAN_UTILS_SAFE_INT_H_ + +#include <botan/exceptn.h> +#include <string> + +namespace Botan { + +class BOTAN_PUBLIC_API(2,0) Integer_Overflow_Detected final : public Exception + { + public: + Integer_Overflow_Detected(const std::string& file, int line) : + Exception("Integer overflow detected at " + file + ":" + std::to_string(line)) + {} + + ErrorType error_type() const noexcept override { return ErrorType::InternalError; } + }; + +inline size_t checked_add(size_t x, size_t y, const char* file, int line) + { + // TODO: use __builtin_x_overflow on GCC and Clang + size_t z = x + y; + if(z < x) + { + throw Integer_Overflow_Detected(file, line); + } + return z; + } + +#define BOTAN_CHECKED_ADD(x,y) checked_add(x,y,__FILE__,__LINE__) + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/simd/info.txt b/comm/third_party/botan/src/lib/utils/simd/info.txt new file mode 100644 index 0000000000..4a7044afc4 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/simd/info.txt @@ -0,0 +1,27 @@ +<defines> +SIMD_32 -> 20131128 +</defines> + +<header:internal> +simd_32.h +</header:internal> + +<isa> +x86_32:sse2 +x86_64:sse2 +x32:sse2 +arm32:neon +arm64:neon +ppc32:altivec +ppc64:altivec +</isa> + +<arch> +x86_32 +x86_64 +x32 +arm32 +arm64 +ppc32 +ppc64 +</arch> diff --git a/comm/third_party/botan/src/lib/utils/simd/simd_32.h b/comm/third_party/botan/src/lib/utils/simd/simd_32.h new file mode 100644 index 0000000000..5cbc32a187 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/simd/simd_32.h @@ -0,0 +1,621 @@ +/* +* Lightweight wrappers for SIMD operations +* (C) 2009,2011,2016,2017,2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SIMD_32_H_ +#define BOTAN_SIMD_32_H_ + +#include <botan/types.h> + +#if defined(BOTAN_TARGET_SUPPORTS_SSE2) + #include <emmintrin.h> + #define BOTAN_SIMD_USE_SSE2 + +#elif defined(BOTAN_TARGET_SUPPORTS_ALTIVEC) + #include <botan/bswap.h> + #include <botan/loadstor.h> + #include <altivec.h> + #undef vector + #undef bool + #define BOTAN_SIMD_USE_ALTIVEC + +#elif defined(BOTAN_TARGET_SUPPORTS_NEON) + #include <botan/cpuid.h> + #include <arm_neon.h> + #define BOTAN_SIMD_USE_NEON + +#else + #error "No SIMD instruction set enabled" +#endif + +#if defined(BOTAN_SIMD_USE_SSE2) + #define BOTAN_SIMD_ISA "sse2" + #define BOTAN_VPERM_ISA "ssse3" + #define BOTAN_CLMUL_ISA "pclmul" +#elif defined(BOTAN_SIMD_USE_NEON) + #if defined(BOTAN_TARGET_ARCH_IS_ARM64) + #define BOTAN_SIMD_ISA "+simd" + #define BOTAN_CLMUL_ISA "+crypto" + #else + #define BOTAN_SIMD_ISA "fpu=neon" + #endif + #define BOTAN_VPERM_ISA BOTAN_SIMD_ISA +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + #define BOTAN_SIMD_ISA "altivec" + #define BOTAN_VPERM_ISA "altivec" + #define BOTAN_CLMUL_ISA "crypto" +#endif + +namespace Botan { + +#if defined(BOTAN_SIMD_USE_SSE2) + typedef __m128i native_simd_type; +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + typedef __vector unsigned int native_simd_type; +#elif defined(BOTAN_SIMD_USE_NEON) + typedef uint32x4_t native_simd_type; +#endif + +/** +* 4x32 bit SIMD register +* +* This class is not a general purpose SIMD type, and only offers +* instructions needed for evaluation of specific crypto primitives. +* For example it does not currently have equality operators of any +* kind. +* +* Implemented for SSE2, VMX (Altivec), and NEON. +*/ +class SIMD_4x32 final + { + public: + + SIMD_4x32& operator=(const SIMD_4x32& other) = default; + SIMD_4x32(const SIMD_4x32& other) = default; + + SIMD_4x32& operator=(SIMD_4x32&& other) = default; + SIMD_4x32(SIMD_4x32&& other) = default; + + /** + * Zero initialize SIMD register with 4 32-bit elements + */ + SIMD_4x32() // zero initialized + { +#if defined(BOTAN_SIMD_USE_SSE2) + m_simd = _mm_setzero_si128(); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + m_simd = vec_splat_u32(0); +#elif defined(BOTAN_SIMD_USE_NEON) + m_simd = vdupq_n_u32(0); +#endif + } + + /** + * Load SIMD register with 4 32-bit elements + */ + explicit SIMD_4x32(const uint32_t B[4]) + { +#if defined(BOTAN_SIMD_USE_SSE2) + m_simd = _mm_loadu_si128(reinterpret_cast<const __m128i*>(B)); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + __vector unsigned int val = { B[0], B[1], B[2], B[3]}; + m_simd = val; +#elif defined(BOTAN_SIMD_USE_NEON) + m_simd = vld1q_u32(B); +#endif + } + + /** + * Load SIMD register with 4 32-bit elements + */ + SIMD_4x32(uint32_t B0, uint32_t B1, uint32_t B2, uint32_t B3) + { +#if defined(BOTAN_SIMD_USE_SSE2) + m_simd = _mm_set_epi32(B3, B2, B1, B0); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + __vector unsigned int val = {B0, B1, B2, B3}; + m_simd = val; +#elif defined(BOTAN_SIMD_USE_NEON) + // Better way to do this? + const uint32_t B[4] = { B0, B1, B2, B3 }; + m_simd = vld1q_u32(B); +#endif + } + + /** + * Load SIMD register with one 32-bit element repeated + */ + static SIMD_4x32 splat(uint32_t B) + { +#if defined(BOTAN_SIMD_USE_SSE2) + return SIMD_4x32(_mm_set1_epi32(B)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vdupq_n_u32(B)); +#else + return SIMD_4x32(B, B, B, B); +#endif + } + + /** + * Load SIMD register with one 8-bit element repeated + */ + static SIMD_4x32 splat_u8(uint8_t B) + { +#if defined(BOTAN_SIMD_USE_SSE2) + return SIMD_4x32(_mm_set1_epi8(B)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vreinterpretq_u32_u8(vdupq_n_u8(B))); +#else + const uint32_t B4 = make_uint32(B, B, B, B); + return SIMD_4x32(B4, B4, B4, B4); +#endif + } + + /** + * Load a SIMD register with little-endian convention + */ + static SIMD_4x32 load_le(const void* in) + { +#if defined(BOTAN_SIMD_USE_SSE2) + return SIMD_4x32(_mm_loadu_si128(reinterpret_cast<const __m128i*>(in))); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + uint32_t R[4]; + Botan::load_le(R, static_cast<const uint8_t*>(in), 4); + return SIMD_4x32(R); +#elif defined(BOTAN_SIMD_USE_NEON) + SIMD_4x32 l(vld1q_u32(static_cast<const uint32_t*>(in))); + return CPUID::is_big_endian() ? l.bswap() : l; +#endif + } + + /** + * Load a SIMD register with big-endian convention + */ + static SIMD_4x32 load_be(const void* in) + { +#if defined(BOTAN_SIMD_USE_SSE2) + return load_le(in).bswap(); + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + uint32_t R[4]; + Botan::load_be(R, static_cast<const uint8_t*>(in), 4); + return SIMD_4x32(R); + +#elif defined(BOTAN_SIMD_USE_NEON) + SIMD_4x32 l(vld1q_u32(static_cast<const uint32_t*>(in))); + return CPUID::is_little_endian() ? l.bswap() : l; +#endif + } + + void store_le(uint32_t out[4]) const + { + this->store_le(reinterpret_cast<uint8_t*>(out)); + } + + void store_le(uint64_t out[2]) const + { + this->store_le(reinterpret_cast<uint8_t*>(out)); + } + + /** + * Load a SIMD register with little-endian convention + */ + void store_le(uint8_t out[]) const + { +#if defined(BOTAN_SIMD_USE_SSE2) + + _mm_storeu_si128(reinterpret_cast<__m128i*>(out), raw()); + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + + union { + __vector unsigned int V; + uint32_t R[4]; + } vec; + vec.V = raw(); + Botan::store_le(out, vec.R[0], vec.R[1], vec.R[2], vec.R[3]); + +#elif defined(BOTAN_SIMD_USE_NEON) + if(CPUID::is_little_endian()) + { + vst1q_u8(out, vreinterpretq_u8_u32(m_simd)); + } + else + { + vst1q_u8(out, vreinterpretq_u8_u32(bswap().m_simd)); + } +#endif + } + + /** + * Load a SIMD register with big-endian convention + */ + void store_be(uint8_t out[]) const + { +#if defined(BOTAN_SIMD_USE_SSE2) + + bswap().store_le(out); + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + + union { + __vector unsigned int V; + uint32_t R[4]; + } vec; + vec.V = m_simd; + Botan::store_be(out, vec.R[0], vec.R[1], vec.R[2], vec.R[3]); + +#elif defined(BOTAN_SIMD_USE_NEON) + if(CPUID::is_little_endian()) + { + vst1q_u8(out, vreinterpretq_u8_u32(bswap().m_simd)); + } + else + { + vst1q_u8(out, vreinterpretq_u8_u32(m_simd)); + } +#endif + } + + /* + * This is used for SHA-2/SHACAL2 + * Return rotr(ROT1) ^ rotr(ROT2) ^ rotr(ROT3) + */ + template<size_t ROT1, size_t ROT2, size_t ROT3> + SIMD_4x32 rho() const + { + const SIMD_4x32 rot1 = this->rotr<ROT1>(); + const SIMD_4x32 rot2 = this->rotr<ROT2>(); + const SIMD_4x32 rot3 = this->rotr<ROT3>(); + return (rot1 ^ rot2 ^ rot3); + } + + /** + * Left rotation by a compile time constant + */ + template<size_t ROT> + SIMD_4x32 rotl() const + { + static_assert(ROT > 0 && ROT < 32, "Invalid rotation constant"); + +#if defined(BOTAN_SIMD_USE_SSE2) + + return SIMD_4x32(_mm_or_si128(_mm_slli_epi32(m_simd, static_cast<int>(ROT)), + _mm_srli_epi32(m_simd, static_cast<int>(32-ROT)))); + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + + const unsigned int r = static_cast<unsigned int>(ROT); + __vector unsigned int rot = {r, r, r, r}; + return SIMD_4x32(vec_rl(m_simd, rot)); + +#elif defined(BOTAN_SIMD_USE_NEON) + +#if defined(BOTAN_TARGET_ARCH_IS_ARM64) + + BOTAN_IF_CONSTEXPR(ROT == 8) + { + const uint8_t maskb[16] = { 3,0,1,2, 7,4,5,6, 11,8,9,10, 15,12,13,14 }; + const uint8x16_t mask = vld1q_u8(maskb); + return SIMD_4x32(vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(m_simd), mask))); + } + else BOTAN_IF_CONSTEXPR(ROT == 16) + { + return SIMD_4x32(vreinterpretq_u32_u16(vrev32q_u16(vreinterpretq_u16_u32(m_simd)))); + } +#endif + return SIMD_4x32(vorrq_u32(vshlq_n_u32(m_simd, static_cast<int>(ROT)), + vshrq_n_u32(m_simd, static_cast<int>(32-ROT)))); +#endif + } + + /** + * Right rotation by a compile time constant + */ + template<size_t ROT> + SIMD_4x32 rotr() const + { + return this->rotl<32-ROT>(); + } + + /** + * Add elements of a SIMD vector + */ + SIMD_4x32 operator+(const SIMD_4x32& other) const + { + SIMD_4x32 retval(*this); + retval += other; + return retval; + } + + /** + * Subtract elements of a SIMD vector + */ + SIMD_4x32 operator-(const SIMD_4x32& other) const + { + SIMD_4x32 retval(*this); + retval -= other; + return retval; + } + + /** + * XOR elements of a SIMD vector + */ + SIMD_4x32 operator^(const SIMD_4x32& other) const + { + SIMD_4x32 retval(*this); + retval ^= other; + return retval; + } + + /** + * Binary OR elements of a SIMD vector + */ + SIMD_4x32 operator|(const SIMD_4x32& other) const + { + SIMD_4x32 retval(*this); + retval |= other; + return retval; + } + + /** + * Binary AND elements of a SIMD vector + */ + SIMD_4x32 operator&(const SIMD_4x32& other) const + { + SIMD_4x32 retval(*this); + retval &= other; + return retval; + } + + void operator+=(const SIMD_4x32& other) + { +#if defined(BOTAN_SIMD_USE_SSE2) + m_simd = _mm_add_epi32(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + m_simd = vec_add(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_NEON) + m_simd = vaddq_u32(m_simd, other.m_simd); +#endif + } + + void operator-=(const SIMD_4x32& other) + { +#if defined(BOTAN_SIMD_USE_SSE2) + m_simd = _mm_sub_epi32(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + m_simd = vec_sub(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_NEON) + m_simd = vsubq_u32(m_simd, other.m_simd); +#endif + } + + void operator^=(const SIMD_4x32& other) + { +#if defined(BOTAN_SIMD_USE_SSE2) + m_simd = _mm_xor_si128(m_simd, other.m_simd); + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + m_simd = vec_xor(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_NEON) + m_simd = veorq_u32(m_simd, other.m_simd); +#endif + } + + void operator|=(const SIMD_4x32& other) + { +#if defined(BOTAN_SIMD_USE_SSE2) + m_simd = _mm_or_si128(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + m_simd = vec_or(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_NEON) + m_simd = vorrq_u32(m_simd, other.m_simd); +#endif + } + + void operator&=(const SIMD_4x32& other) + { +#if defined(BOTAN_SIMD_USE_SSE2) + m_simd = _mm_and_si128(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + m_simd = vec_and(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_NEON) + m_simd = vandq_u32(m_simd, other.m_simd); +#endif + } + + + template<int SHIFT> SIMD_4x32 shl() const + { + static_assert(SHIFT > 0 && SHIFT <= 31, "Invalid shift count"); + +#if defined(BOTAN_SIMD_USE_SSE2) + return SIMD_4x32(_mm_slli_epi32(m_simd, SHIFT)); + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const unsigned int s = static_cast<unsigned int>(SHIFT); + const __vector unsigned int shifts = {s, s, s, s}; + return SIMD_4x32(vec_sl(m_simd, shifts)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vshlq_n_u32(m_simd, SHIFT)); +#endif + } + + template<int SHIFT> SIMD_4x32 shr() const + { +#if defined(BOTAN_SIMD_USE_SSE2) + return SIMD_4x32(_mm_srli_epi32(m_simd, SHIFT)); + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const unsigned int s = static_cast<unsigned int>(SHIFT); + const __vector unsigned int shifts = {s, s, s, s}; + return SIMD_4x32(vec_sr(m_simd, shifts)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vshrq_n_u32(m_simd, SHIFT)); +#endif + } + + SIMD_4x32 operator~() const + { +#if defined(BOTAN_SIMD_USE_SSE2) + return SIMD_4x32(_mm_xor_si128(m_simd, _mm_set1_epi32(0xFFFFFFFF))); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + return SIMD_4x32(vec_nor(m_simd, m_simd)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vmvnq_u32(m_simd)); +#endif + } + + // (~reg) & other + SIMD_4x32 andc(const SIMD_4x32& other) const + { +#if defined(BOTAN_SIMD_USE_SSE2) + return SIMD_4x32(_mm_andnot_si128(m_simd, other.m_simd)); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + /* + AltiVec does arg1 & ~arg2 rather than SSE's ~arg1 & arg2 + so swap the arguments + */ + return SIMD_4x32(vec_andc(other.m_simd, m_simd)); +#elif defined(BOTAN_SIMD_USE_NEON) + // NEON is also a & ~b + return SIMD_4x32(vbicq_u32(other.m_simd, m_simd)); +#endif + } + + /** + * Return copy *this with each word byte swapped + */ + SIMD_4x32 bswap() const + { +#if defined(BOTAN_SIMD_USE_SSE2) + + __m128i T = m_simd; + T = _mm_shufflehi_epi16(T, _MM_SHUFFLE(2, 3, 0, 1)); + T = _mm_shufflelo_epi16(T, _MM_SHUFFLE(2, 3, 0, 1)); + return SIMD_4x32(_mm_or_si128(_mm_srli_epi16(T, 8), _mm_slli_epi16(T, 8))); + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + + union { + __vector unsigned int V; + uint32_t R[4]; + } vec; + + vec.V = m_simd; + bswap_4(vec.R); + return SIMD_4x32(vec.R[0], vec.R[1], vec.R[2], vec.R[3]); + +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(m_simd)))); +#endif + } + + template<size_t I> + SIMD_4x32 shift_elems_left() const + { + static_assert(I <= 3, "Invalid shift count"); + +#if defined(BOTAN_SIMD_USE_SSE2) + return SIMD_4x32(_mm_slli_si128(raw(), 4*I)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vextq_u32(vdupq_n_u32(0), raw(), 4-I)); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const __vector unsigned int zero = vec_splat_u32(0); + + const __vector unsigned char shuf[3] = { + { 16, 17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, + { 16, 17, 18, 19, 20, 21, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 0, 1, 2, 3 }, + }; + + return SIMD_4x32(vec_perm(raw(), zero, shuf[I-1])); +#endif + } + + template<size_t I> + SIMD_4x32 shift_elems_right() const + { + static_assert(I <= 3, "Invalid shift count"); + +#if defined(BOTAN_SIMD_USE_SSE2) + return SIMD_4x32(_mm_srli_si128(raw(), 4*I)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vextq_u32(raw(), vdupq_n_u32(0), I)); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const __vector unsigned int zero = vec_splat_u32(0); + + const __vector unsigned char shuf[3] = { + { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }, + { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }, + { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 }, + }; + + return SIMD_4x32(vec_perm(raw(), zero, shuf[I-1])); +#endif + } + + /** + * 4x4 Transposition on SIMD registers + */ + static void transpose(SIMD_4x32& B0, SIMD_4x32& B1, + SIMD_4x32& B2, SIMD_4x32& B3) + { +#if defined(BOTAN_SIMD_USE_SSE2) + const __m128i T0 = _mm_unpacklo_epi32(B0.m_simd, B1.m_simd); + const __m128i T1 = _mm_unpacklo_epi32(B2.m_simd, B3.m_simd); + const __m128i T2 = _mm_unpackhi_epi32(B0.m_simd, B1.m_simd); + const __m128i T3 = _mm_unpackhi_epi32(B2.m_simd, B3.m_simd); + + B0.m_simd = _mm_unpacklo_epi64(T0, T1); + B1.m_simd = _mm_unpackhi_epi64(T0, T1); + B2.m_simd = _mm_unpacklo_epi64(T2, T3); + B3.m_simd = _mm_unpackhi_epi64(T2, T3); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const __vector unsigned int T0 = vec_mergeh(B0.m_simd, B2.m_simd); + const __vector unsigned int T1 = vec_mergeh(B1.m_simd, B3.m_simd); + const __vector unsigned int T2 = vec_mergel(B0.m_simd, B2.m_simd); + const __vector unsigned int T3 = vec_mergel(B1.m_simd, B3.m_simd); + + B0.m_simd = vec_mergeh(T0, T1); + B1.m_simd = vec_mergel(T0, T1); + B2.m_simd = vec_mergeh(T2, T3); + B3.m_simd = vec_mergel(T2, T3); + +#elif defined(BOTAN_SIMD_USE_NEON) && defined(BOTAN_TARGET_ARCH_IS_ARM32) + const uint32x4x2_t T0 = vzipq_u32(B0.m_simd, B2.m_simd); + const uint32x4x2_t T1 = vzipq_u32(B1.m_simd, B3.m_simd); + const uint32x4x2_t O0 = vzipq_u32(T0.val[0], T1.val[0]); + const uint32x4x2_t O1 = vzipq_u32(T0.val[1], T1.val[1]); + + B0.m_simd = O0.val[0]; + B1.m_simd = O0.val[1]; + B2.m_simd = O1.val[0]; + B3.m_simd = O1.val[1]; + +#elif defined(BOTAN_SIMD_USE_NEON) && defined(BOTAN_TARGET_ARCH_IS_ARM64) + const uint32x4_t T0 = vzip1q_u32(B0.m_simd, B2.m_simd); + const uint32x4_t T2 = vzip2q_u32(B0.m_simd, B2.m_simd); + const uint32x4_t T1 = vzip1q_u32(B1.m_simd, B3.m_simd); + const uint32x4_t T3 = vzip2q_u32(B1.m_simd, B3.m_simd); + + B0.m_simd = vzip1q_u32(T0, T1); + B1.m_simd = vzip2q_u32(T0, T1); + B2.m_simd = vzip1q_u32(T2, T3); + B3.m_simd = vzip2q_u32(T2, T3); +#endif + } + + native_simd_type raw() const BOTAN_FUNC_ISA(BOTAN_SIMD_ISA) { return m_simd; } + + explicit SIMD_4x32(native_simd_type x) : m_simd(x) {} + private: + native_simd_type m_simd; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/simd/simd_avx2/info.txt b/comm/third_party/botan/src/lib/utils/simd/simd_avx2/info.txt new file mode 100644 index 0000000000..13a9ae4cbb --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/simd/simd_avx2/info.txt @@ -0,0 +1,21 @@ +<defines> +SIMD_AVX2 -> 20180824 +</defines> + +<isa> +avx2 +</isa> + +<header:internal> +simd_avx2.h +</header:internal> + +<cc> +gcc +clang +msvc + +# Intel C++ 2020 doesn't support target attribute on overloaded +# operators, see GH #2260 +#icc +</cc> diff --git a/comm/third_party/botan/src/lib/utils/simd/simd_avx2/simd_avx2.h b/comm/third_party/botan/src/lib/utils/simd/simd_avx2/simd_avx2.h new file mode 100644 index 0000000000..3498c2ad08 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/simd/simd_avx2/simd_avx2.h @@ -0,0 +1,299 @@ +/* +* (C) 2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SIMD_AVX2_H_ +#define BOTAN_SIMD_AVX2_H_ + +#include <botan/types.h> +#include <immintrin.h> + +namespace Botan { + +class SIMD_8x32 final + { + public: + + SIMD_8x32& operator=(const SIMD_8x32& other) = default; + SIMD_8x32(const SIMD_8x32& other) = default; + + SIMD_8x32& operator=(SIMD_8x32&& other) = default; + SIMD_8x32(SIMD_8x32&& other) = default; + + BOTAN_FUNC_ISA("avx2") + BOTAN_FORCE_INLINE SIMD_8x32() + { + m_avx2 = _mm256_setzero_si256(); + } + + BOTAN_FUNC_ISA("avx2") + explicit SIMD_8x32(const uint32_t B[8]) + { + m_avx2 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(B)); + } + + BOTAN_FUNC_ISA("avx2") + explicit SIMD_8x32(uint32_t B0, uint32_t B1, uint32_t B2, uint32_t B3, + uint32_t B4, uint32_t B5, uint32_t B6, uint32_t B7) + { + m_avx2 = _mm256_set_epi32(B7, B6, B5, B4, B3, B2, B1, B0); + } + + BOTAN_FUNC_ISA("avx2") + static SIMD_8x32 splat(uint32_t B) + { + return SIMD_8x32(_mm256_set1_epi32(B)); + } + + BOTAN_FUNC_ISA("avx2") + static SIMD_8x32 load_le(const uint8_t* in) + { + return SIMD_8x32(_mm256_loadu_si256(reinterpret_cast<const __m256i*>(in))); + } + + BOTAN_FUNC_ISA("avx2") + static SIMD_8x32 load_be(const uint8_t* in) + { + return load_le(in).bswap(); + } + + BOTAN_FUNC_ISA("avx2") + void store_le(uint8_t out[]) const + { + _mm256_storeu_si256(reinterpret_cast<__m256i*>(out), m_avx2); + } + + BOTAN_FUNC_ISA("avx2") + void store_be(uint8_t out[]) const + { + bswap().store_le(out); + } + + template<size_t ROT> + BOTAN_FUNC_ISA("avx2") + SIMD_8x32 rotl() const + { + static_assert(ROT > 0 && ROT < 32, "Invalid rotation constant"); + +#if defined(__AVX512VL__) + return SIMD_8x32(_mm256_rol_epi32(m_avx2, ROT)); +#else + BOTAN_IF_CONSTEXPR(ROT == 8) + { + const __m256i shuf_rotl_8 = _mm256_set_epi8(14, 13, 12, 15, 10, 9, 8, 11, 6, 5, 4, 7, 2, 1, 0, 3, + 14, 13, 12, 15, 10, 9, 8, 11, 6, 5, 4, 7, 2, 1, 0, 3); + + return SIMD_8x32(_mm256_shuffle_epi8(m_avx2, shuf_rotl_8)); + } + else BOTAN_IF_CONSTEXPR(ROT == 16) + { + const __m256i shuf_rotl_16 = _mm256_set_epi8(13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2, + 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2); + + return SIMD_8x32(_mm256_shuffle_epi8(m_avx2, shuf_rotl_16)); + } + else + { + return SIMD_8x32(_mm256_or_si256(_mm256_slli_epi32(m_avx2, static_cast<int>(ROT)), + _mm256_srli_epi32(m_avx2, static_cast<int>(32-ROT)))); + } +#endif + } + + template<size_t ROT> + BOTAN_FUNC_ISA("avx2") + SIMD_8x32 rotr() const + { + return this->rotl<32-ROT>(); + } + + template<size_t ROT1, size_t ROT2, size_t ROT3> + SIMD_8x32 BOTAN_FUNC_ISA("avx2") rho() const + { + SIMD_8x32 res; + + const SIMD_8x32 rot1 = this->rotr<ROT1>(); + const SIMD_8x32 rot2 = this->rotr<ROT2>(); + const SIMD_8x32 rot3 = this->rotr<ROT3>(); + + return rot1 ^ rot2 ^ rot3; + } + + BOTAN_FUNC_ISA("avx2") + SIMD_8x32 operator+(const SIMD_8x32& other) const + { + SIMD_8x32 retval(*this); + retval += other; + return retval; + } + + BOTAN_FUNC_ISA("avx2") + SIMD_8x32 operator-(const SIMD_8x32& other) const + { + SIMD_8x32 retval(*this); + retval -= other; + return retval; + } + + BOTAN_FUNC_ISA("avx2") + SIMD_8x32 operator^(const SIMD_8x32& other) const + { + SIMD_8x32 retval(*this); + retval ^= other; + return retval; + } + + BOTAN_FUNC_ISA("avx2") + SIMD_8x32 operator|(const SIMD_8x32& other) const + { + SIMD_8x32 retval(*this); + retval |= other; + return retval; + } + + BOTAN_FUNC_ISA("avx2") + SIMD_8x32 operator&(const SIMD_8x32& other) const + { + SIMD_8x32 retval(*this); + retval &= other; + return retval; + } + + BOTAN_FUNC_ISA("avx2") + void operator+=(const SIMD_8x32& other) + { + m_avx2 = _mm256_add_epi32(m_avx2, other.m_avx2); + } + + BOTAN_FUNC_ISA("avx2") + void operator-=(const SIMD_8x32& other) + { + m_avx2 = _mm256_sub_epi32(m_avx2, other.m_avx2); + } + + BOTAN_FUNC_ISA("avx2") + void operator^=(const SIMD_8x32& other) + { + m_avx2 = _mm256_xor_si256(m_avx2, other.m_avx2); + } + + BOTAN_FUNC_ISA("avx2") + void operator|=(const SIMD_8x32& other) + { + m_avx2 = _mm256_or_si256(m_avx2, other.m_avx2); + } + + BOTAN_FUNC_ISA("avx2") + void operator&=(const SIMD_8x32& other) + { + m_avx2 = _mm256_and_si256(m_avx2, other.m_avx2); + } + + template<int SHIFT> BOTAN_FUNC_ISA("avx2") SIMD_8x32 shl() const + { + return SIMD_8x32(_mm256_slli_epi32(m_avx2, SHIFT)); + } + + template<int SHIFT> BOTAN_FUNC_ISA("avx2") SIMD_8x32 shr() const + { + return SIMD_8x32(_mm256_srli_epi32(m_avx2, SHIFT)); + } + + BOTAN_FUNC_ISA("avx2") + SIMD_8x32 operator~() const + { + return SIMD_8x32(_mm256_xor_si256(m_avx2, _mm256_set1_epi32(0xFFFFFFFF))); + } + + // (~reg) & other + BOTAN_FUNC_ISA("avx2") + SIMD_8x32 andc(const SIMD_8x32& other) const + { + return SIMD_8x32(_mm256_andnot_si256(m_avx2, other.m_avx2)); + } + + BOTAN_FUNC_ISA("avx2") + SIMD_8x32 bswap() const + { + const uint8_t BSWAP_MASK[32] = { 3, 2, 1, 0, + 7, 6, 5, 4, + 11, 10, 9, 8, + 15, 14, 13, 12, + 19, 18, 17, 16, + 23, 22, 21, 20, + 27, 26, 25, 24, + 31, 30, 29, 28 }; + + const __m256i bswap = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(BSWAP_MASK)); + + const __m256i output = _mm256_shuffle_epi8(m_avx2, bswap); + + return SIMD_8x32(output); + } + + BOTAN_FUNC_ISA("avx2") + static void transpose(SIMD_8x32& B0, SIMD_8x32& B1, + SIMD_8x32& B2, SIMD_8x32& B3) + { + const __m256i T0 = _mm256_unpacklo_epi32(B0.m_avx2, B1.m_avx2); + const __m256i T1 = _mm256_unpacklo_epi32(B2.m_avx2, B3.m_avx2); + const __m256i T2 = _mm256_unpackhi_epi32(B0.m_avx2, B1.m_avx2); + const __m256i T3 = _mm256_unpackhi_epi32(B2.m_avx2, B3.m_avx2); + + B0.m_avx2 = _mm256_unpacklo_epi64(T0, T1); + B1.m_avx2 = _mm256_unpackhi_epi64(T0, T1); + B2.m_avx2 = _mm256_unpacklo_epi64(T2, T3); + B3.m_avx2 = _mm256_unpackhi_epi64(T2, T3); + } + + BOTAN_FUNC_ISA("avx2") + static void transpose(SIMD_8x32& B0, SIMD_8x32& B1, + SIMD_8x32& B2, SIMD_8x32& B3, + SIMD_8x32& B4, SIMD_8x32& B5, + SIMD_8x32& B6, SIMD_8x32& B7) + { + transpose(B0, B1, B2, B3); + transpose(B4, B5, B6, B7); + + swap_tops(B0, B4); + swap_tops(B1, B5); + swap_tops(B2, B6); + swap_tops(B3, B7); + } + + BOTAN_FUNC_ISA("avx2") + static void reset_registers() + { + _mm256_zeroupper(); + } + + BOTAN_FUNC_ISA("avx2") + static void zero_registers() + { + _mm256_zeroall(); + } + + __m256i BOTAN_FUNC_ISA("avx2") handle() const { return m_avx2; } + + BOTAN_FUNC_ISA("avx2") + SIMD_8x32(__m256i x) : m_avx2(x) {} + + private: + + BOTAN_FUNC_ISA("avx2") + static void swap_tops(SIMD_8x32& A, SIMD_8x32& B) + { + SIMD_8x32 T0 = _mm256_permute2x128_si256(A.handle(), B.handle(), 0 + (2 << 4)); + SIMD_8x32 T1 = _mm256_permute2x128_si256(A.handle(), B.handle(), 1 + (3 << 4)); + A = T0; + B = T1; + } + + __m256i m_avx2; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/socket/info.txt b/comm/third_party/botan/src/lib/utils/socket/info.txt new file mode 100644 index 0000000000..3862f0f74e --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/socket/info.txt @@ -0,0 +1,18 @@ +<defines> +SOCKETS -> 20171216 +</defines> + +<header:internal> +uri.h +socket.h +socket_udp.h +</header:internal> + +<libs> +linux -> rt +mingw -> ws2_32 +windows -> ws2_32 +haiku -> network +solaris -> socket,nsl +qnx -> socket +</libs> diff --git a/comm/third_party/botan/src/lib/utils/socket/socket.cpp b/comm/third_party/botan/src/lib/utils/socket/socket.cpp new file mode 100644 index 0000000000..bc632259a6 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/socket/socket.cpp @@ -0,0 +1,371 @@ +/* +* (C) 2015,2016,2017 Jack Lloyd +* (C) 2016 Daniel Neus +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/socket.h> +#include <botan/exceptn.h> +#include <botan/mem_ops.h> +#include <chrono> + +#if defined(BOTAN_HAS_BOOST_ASIO) + /* + * We don't need serial port support anyway, and asking for it causes + * macro conflicts with termios.h when this file is included in the + * amalgamation. + */ + #define BOOST_ASIO_DISABLE_SERIAL_PORT + #include <boost/asio.hpp> + #include <boost/asio/system_timer.hpp> + +#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) + #include <sys/socket.h> + #include <sys/time.h> + #include <netinet/in.h> + #include <netdb.h> + #include <string.h> + #include <unistd.h> + #include <errno.h> + #include <fcntl.h> + +#elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2) + #include <ws2tcpip.h> +#endif + +namespace Botan { + +namespace { + +#if defined(BOTAN_HAS_BOOST_ASIO) + +class Asio_Socket final : public OS::Socket + { + public: + Asio_Socket(const std::string& hostname, + const std::string& service, + std::chrono::milliseconds timeout) : + m_timeout(timeout), m_timer(m_io), m_tcp(m_io) + { + m_timer.expires_from_now(m_timeout); + check_timeout(); + + boost::asio::ip::tcp::resolver resolver(m_io); + boost::asio::ip::tcp::resolver::query query(hostname, service); + boost::asio::ip::tcp::resolver::iterator dns_iter = resolver.resolve(query); + + boost::system::error_code ec = boost::asio::error::would_block; + + auto connect_cb = [&ec](const boost::system::error_code& e, + boost::asio::ip::tcp::resolver::iterator) { ec = e; }; + + boost::asio::async_connect(m_tcp, dns_iter, connect_cb); + + while(ec == boost::asio::error::would_block) + { + m_io.run_one(); + } + + if(ec) + throw boost::system::system_error(ec); + if(m_tcp.is_open() == false) + throw System_Error("Connection to host " + hostname + " failed"); + } + + void write(const uint8_t buf[], size_t len) override + { + m_timer.expires_from_now(m_timeout); + + boost::system::error_code ec = boost::asio::error::would_block; + + m_tcp.async_send(boost::asio::buffer(buf, len), + [&ec](boost::system::error_code e, size_t) { ec = e; }); + + while(ec == boost::asio::error::would_block) { m_io.run_one(); } + + if(ec) + { + throw boost::system::system_error(ec); + } + } + + size_t read(uint8_t buf[], size_t len) override + { + m_timer.expires_from_now(m_timeout); + + boost::system::error_code ec = boost::asio::error::would_block; + size_t got = 0; + + m_tcp.async_read_some(boost::asio::buffer(buf, len), + [&](boost::system::error_code cb_ec, size_t cb_got) { ec = cb_ec; got = cb_got; }); + + while(ec == boost::asio::error::would_block) { m_io.run_one(); } + + if(ec) + { + if(ec == boost::asio::error::eof) + return 0; + throw boost::system::system_error(ec); // Some other error. + } + + return got; + } + + private: + void check_timeout() + { + if(m_tcp.is_open() && m_timer.expires_at() < std::chrono::system_clock::now()) + { + boost::system::error_code err; + m_tcp.close(err); + } + + m_timer.async_wait(std::bind(&Asio_Socket::check_timeout, this)); + } + + const std::chrono::milliseconds m_timeout; + boost::asio::io_service m_io; + boost::asio::system_timer m_timer; + boost::asio::ip::tcp::socket m_tcp; + }; + +#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2) + +class BSD_Socket final : public OS::Socket + { + private: +#if defined(BOTAN_TARGET_OS_HAS_WINSOCK2) + typedef SOCKET socket_type; + typedef int socket_op_ret_type; + typedef int socklen_type; + typedef int sendrecv_len_type; + static socket_type invalid_socket() { return INVALID_SOCKET; } + static void close_socket(socket_type s) { ::closesocket(s); } + static std::string get_last_socket_error() { return std::to_string(::WSAGetLastError()); } + + static bool nonblocking_connect_in_progress() + { + return (::WSAGetLastError() == WSAEWOULDBLOCK); + } + + static void set_nonblocking(socket_type s) + { + u_long nonblocking = 1; + ::ioctlsocket(s, FIONBIO, &nonblocking); + } + + static void socket_init() + { + WSAData wsa_data; + WORD wsa_version = MAKEWORD(2, 2); + + if (::WSAStartup(wsa_version, &wsa_data) != 0) + { + throw System_Error("WSAStartup() failed", WSAGetLastError()); + } + + if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) + { + ::WSACleanup(); + throw System_Error("Could not find a usable version of Winsock.dll"); + } + } + + static void socket_fini() + { + ::WSACleanup(); + } +#else + typedef int socket_type; + typedef ssize_t socket_op_ret_type; + typedef socklen_t socklen_type; + typedef size_t sendrecv_len_type; + static socket_type invalid_socket() { return -1; } + static void close_socket(socket_type s) { ::close(s); } + static std::string get_last_socket_error() { return ::strerror(errno); } + static bool nonblocking_connect_in_progress() { return (errno == EINPROGRESS); } + static void set_nonblocking(socket_type s) + { + if(::fcntl(s, F_SETFL, O_NONBLOCK) < 0) + throw System_Error("Setting socket to non-blocking state failed", errno); + } + + static void socket_init() {} + static void socket_fini() {} +#endif + + public: + BSD_Socket(const std::string& hostname, + const std::string& service, + std::chrono::microseconds timeout) : m_timeout(timeout) + { + socket_init(); + + m_socket = invalid_socket(); + + addrinfo hints; + clear_mem(&hints, 1); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + addrinfo* res; + + int rc = ::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res); + + if(rc != 0) + { + throw System_Error("Name resolution failed for " + hostname, rc); + } + + for(addrinfo* rp = res; (m_socket == invalid_socket()) && (rp != nullptr); rp = rp->ai_next) + { + if(rp->ai_family != AF_INET && rp->ai_family != AF_INET6) + continue; + + m_socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + if(m_socket == invalid_socket()) + { + // unsupported socket type? + continue; + } + + set_nonblocking(m_socket); + + int err = ::connect(m_socket, rp->ai_addr, static_cast<socklen_type>(rp->ai_addrlen)); + + if(err == -1) + { + int active = 0; + if(nonblocking_connect_in_progress()) + { + struct timeval timeout_tv = make_timeout_tv(); + fd_set write_set; + FD_ZERO(&write_set); + // Weirdly, Winsock uses a SOCKET type but wants FD_SET to get an int instead + FD_SET(static_cast<int>(m_socket), &write_set); + + active = ::select(static_cast<int>(m_socket + 1), nullptr, &write_set, nullptr, &timeout_tv); + + if(active) + { + int socket_error = 0; + socklen_t len = sizeof(socket_error); + + if(::getsockopt(m_socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&socket_error), &len) < 0) + throw System_Error("Error calling getsockopt", errno); + + if(socket_error != 0) + { + active = 0; + } + } + } + + if(active == 0) + { + close_socket(m_socket); + m_socket = invalid_socket(); + continue; + } + } + } + + ::freeaddrinfo(res); + + if(m_socket == invalid_socket()) + { + throw System_Error("Connecting to " + hostname + + " for service " + service + " failed", errno); + } + } + + ~BSD_Socket() + { + close_socket(m_socket); + m_socket = invalid_socket(); + socket_fini(); + } + + void write(const uint8_t buf[], size_t len) override + { + fd_set write_set; + FD_ZERO(&write_set); + FD_SET(m_socket, &write_set); + + size_t sent_so_far = 0; + while(sent_so_far != len) + { + struct timeval timeout = make_timeout_tv(); + int active = ::select(static_cast<int>(m_socket + 1), nullptr, &write_set, nullptr, &timeout); + + if(active == 0) + throw System_Error("Timeout during socket write"); + + const size_t left = len - sent_so_far; + socket_op_ret_type sent = ::send(m_socket, cast_uint8_ptr_to_char(&buf[sent_so_far]), static_cast<sendrecv_len_type>(left), 0); + if(sent < 0) + throw System_Error("Socket write failed", errno); + else + sent_so_far += static_cast<size_t>(sent); + } + } + + size_t read(uint8_t buf[], size_t len) override + { + fd_set read_set; + FD_ZERO(&read_set); + FD_SET(m_socket, &read_set); + + struct timeval timeout = make_timeout_tv(); + int active = ::select(static_cast<int>(m_socket + 1), &read_set, nullptr, nullptr, &timeout); + + if(active == 0) + throw System_Error("Timeout during socket read"); + + socket_op_ret_type got = ::recv(m_socket, cast_uint8_ptr_to_char(buf), static_cast<sendrecv_len_type>(len), 0); + + if(got < 0) + throw System_Error("Socket read failed", errno); + + return static_cast<size_t>(got); + } + + private: + struct timeval make_timeout_tv() const + { + struct timeval tv; + tv.tv_sec = static_cast<decltype(timeval::tv_sec)>(m_timeout.count() / 1000000); + tv.tv_usec = static_cast<decltype(timeval::tv_usec)>(m_timeout.count() % 1000000);; + return tv; + } + + const std::chrono::microseconds m_timeout; + socket_type m_socket; + }; + +#endif + +} + +std::unique_ptr<OS::Socket> +OS::open_socket(const std::string& hostname, + const std::string& service, + std::chrono::milliseconds timeout) + { +#if defined(BOTAN_HAS_BOOST_ASIO) + return std::unique_ptr<OS::Socket>(new Asio_Socket(hostname, service, timeout)); + +#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2) + return std::unique_ptr<OS::Socket>(new BSD_Socket(hostname, service, timeout)); + +#else + BOTAN_UNUSED(hostname); + BOTAN_UNUSED(service); + BOTAN_UNUSED(timeout); + // No sockets for you + return std::unique_ptr<Socket>(); +#endif + } + +} diff --git a/comm/third_party/botan/src/lib/utils/socket/socket.h b/comm/third_party/botan/src/lib/utils/socket/socket.h new file mode 100644 index 0000000000..03a951478a --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/socket/socket.h @@ -0,0 +1,64 @@ +/* +* OS specific utility functions +* (C) 2015,2016,2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SOCKET_H_ +#define BOTAN_SOCKET_H_ + +#include <botan/types.h> +#include <string> +#include <chrono> + +namespace Botan { + +namespace OS { + +/* +* This header is internal (not installed) and these functions are not +* intended to be called by applications. However they are given public +* visibility (using BOTAN_TEST_API macro) for the tests. This also probably +* allows them to be overridden by the application on ELF systems, but +* this hasn't been tested. +*/ + + +/** +* A wrapper around a simple blocking TCP socket +*/ +class BOTAN_TEST_API Socket + { + public: + /** + * The socket will be closed upon destruction + */ + virtual ~Socket() = default; + + /** + * Write to the socket. Blocks until all bytes sent. + * Throws on error. + */ + virtual void write(const uint8_t buf[], size_t len) = 0; + + /** + * Reads up to len bytes, returns bytes written to buf. + * Returns 0 on EOF. Throws on error. + */ + virtual size_t read(uint8_t buf[], size_t len) = 0; + }; + +/** +* Open up a socket. Will throw on error. Returns null if sockets are +* not available on this platform. +*/ +std::unique_ptr<Socket> +BOTAN_TEST_API open_socket(const std::string& hostname, + const std::string& service, + std::chrono::milliseconds timeout); + +} // OS +} // Botan + +#endif diff --git a/comm/third_party/botan/src/lib/utils/socket/socket_udp.cpp b/comm/third_party/botan/src/lib/utils/socket/socket_udp.cpp new file mode 100644 index 0000000000..fbbdd9abbc --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/socket/socket_udp.cpp @@ -0,0 +1,344 @@ +/* +* (C) 2015,2016,2017 Jack Lloyd +* (C) 2016 Daniel Neus +* (C) 2019 Nuno Goncalves <nunojpg@gmail.com> +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/socket_udp.h> +#include <botan/internal/uri.h> +#include <botan/exceptn.h> +#include <botan/mem_ops.h> +#include <chrono> + +#if defined(BOTAN_HAS_BOOST_ASIO) + /* + * We don't need serial port support anyway, and asking for it + * causes macro conflicts with Darwin's termios.h when this + * file is included in the amalgamation. GH #350 + */ + #define BOOST_ASIO_DISABLE_SERIAL_PORT + #include <boost/asio.hpp> + #include <boost/asio/system_timer.hpp> +#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) + #include <sys/socket.h> + #include <sys/time.h> + #include <netinet/in.h> + #include <netdb.h> + #include <string.h> + #include <unistd.h> + #include <errno.h> + #include <fcntl.h> + +#elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2) + #include <ws2tcpip.h> +#endif + +namespace Botan { + +namespace { + +#if defined(BOTAN_HAS_BOOST_ASIO) +class Asio_SocketUDP final : public OS::SocketUDP + { + public: + Asio_SocketUDP(const std::string& hostname, + const std::string& service, + std::chrono::microseconds timeout) : + m_timeout(timeout), m_timer(m_io), m_udp(m_io) + { + m_timer.expires_from_now(m_timeout); + check_timeout(); + + boost::asio::ip::udp::resolver resolver(m_io); + boost::asio::ip::udp::resolver::query query(hostname, service); + boost::asio::ip::udp::resolver::iterator dns_iter = resolver.resolve(query); + + boost::system::error_code ec = boost::asio::error::would_block; + + auto connect_cb = [&ec](const boost::system::error_code& e, + boost::asio::ip::udp::resolver::iterator) { ec = e; }; + + boost::asio::async_connect(m_udp, dns_iter, connect_cb); + + while(ec == boost::asio::error::would_block) + { + m_io.run_one(); + } + + if(ec) + { throw boost::system::system_error(ec); } + if(m_udp.is_open() == false) + { throw System_Error("Connection to host " + hostname + " failed"); } + } + + void write(const uint8_t buf[], size_t len) override + { + m_timer.expires_from_now(m_timeout); + + boost::system::error_code ec = boost::asio::error::would_block; + + m_udp.async_send(boost::asio::buffer(buf, len), + [&ec](boost::system::error_code e, size_t) { ec = e; }); + + while(ec == boost::asio::error::would_block) + { + m_io.run_one(); + } + + if(ec) + { + throw boost::system::system_error(ec); + } + } + + size_t read(uint8_t buf[], size_t len) override + { + m_timer.expires_from_now(m_timeout); + + boost::system::error_code ec = boost::asio::error::would_block; + size_t got = 0; + + m_udp.async_receive(boost::asio::buffer(buf, len), + [&](boost::system::error_code cb_ec, size_t cb_got) { ec = cb_ec; got = cb_got; }); + + while(ec == boost::asio::error::would_block) + { + m_io.run_one(); + } + + if(ec) + { + if(ec == boost::asio::error::eof) + { return 0; } + throw boost::system::system_error(ec); // Some other error. + } + + return got; + } + + private: + void check_timeout() + { + if(m_udp.is_open() && m_timer.expires_at() < std::chrono::system_clock::now()) + { + boost::system::error_code err; + m_udp.close(err); + } + + m_timer.async_wait(std::bind(&Asio_SocketUDP::check_timeout, this)); + } + + const std::chrono::microseconds m_timeout; + boost::asio::io_service m_io; + boost::asio::system_timer m_timer; + boost::asio::ip::udp::socket m_udp; + }; +#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2) +class BSD_SocketUDP final : public OS::SocketUDP + { + public: + BSD_SocketUDP(const std::string& hostname, + const std::string& service, + std::chrono::microseconds timeout) : m_timeout(timeout) + { + socket_init(); + + m_socket = invalid_socket(); + + addrinfo* res; + addrinfo hints; + clear_mem(&hints, 1); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + int rc = ::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res); + + if(rc != 0) + { + throw System_Error("Name resolution failed for " + hostname, rc); + } + + for(addrinfo* rp = res; (m_socket == invalid_socket()) && (rp != nullptr); rp = rp->ai_next) + { + if(rp->ai_family != AF_INET && rp->ai_family != AF_INET6) + { continue; } + + m_socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + if(m_socket == invalid_socket()) + { + // unsupported socket type? + continue; + } + + set_nonblocking(m_socket); + memcpy(&sa, res->ai_addr, res->ai_addrlen); + salen = static_cast<socklen_t>(res->ai_addrlen); + } + + ::freeaddrinfo(res); + + if(m_socket == invalid_socket()) + { + throw System_Error("Connecting to " + hostname + + " for service " + service + " failed", errno); + } + } + + ~BSD_SocketUDP() + { + close_socket(m_socket); + m_socket = invalid_socket(); + socket_fini(); + } + + void write(const uint8_t buf[], size_t len) override + { + fd_set write_set; + FD_ZERO(&write_set); + FD_SET(m_socket, &write_set); + + size_t sent_so_far = 0; + while(sent_so_far != len) + { + struct timeval timeout = make_timeout_tv(); + int active = ::select(static_cast<int>(m_socket + 1), nullptr, &write_set, nullptr, &timeout); + + if(active == 0) + { throw System_Error("Timeout during socket write"); } + + const size_t left = len - sent_so_far; + socket_op_ret_type sent = ::sendto(m_socket, cast_uint8_ptr_to_char(buf + sent_so_far), + static_cast<sendrecv_len_type>(left), 0, + reinterpret_cast<sockaddr*>(&sa), salen); + if(sent < 0) + { throw System_Error("Socket write failed", errno); } + else + { sent_so_far += static_cast<size_t>(sent); } + } + } + + size_t read(uint8_t buf[], size_t len) override + { + fd_set read_set; + FD_ZERO(&read_set); + FD_SET(m_socket, &read_set); + + struct timeval timeout = make_timeout_tv(); + int active = ::select(static_cast<int>(m_socket + 1), &read_set, nullptr, nullptr, &timeout); + + if(active == 0) + { throw System_Error("Timeout during socket read"); } + + socket_op_ret_type got = ::recvfrom(m_socket, cast_uint8_ptr_to_char(buf), static_cast<sendrecv_len_type>(len), 0, nullptr, nullptr); + + if(got < 0) + { throw System_Error("Socket read failed", errno); } + + return static_cast<size_t>(got); + } + + private: +#if defined(BOTAN_TARGET_OS_HAS_WINSOCK2) + typedef SOCKET socket_type; + typedef int socket_op_ret_type; + typedef int sendrecv_len_type; + static socket_type invalid_socket() { return INVALID_SOCKET; } + static void close_socket(socket_type s) { ::closesocket(s); } + static std::string get_last_socket_error() { return std::to_string(::WSAGetLastError()); } + + static bool nonblocking_connect_in_progress() + { + return (::WSAGetLastError() == WSAEWOULDBLOCK); + } + + static void set_nonblocking(socket_type s) + { + u_long nonblocking = 1; + ::ioctlsocket(s, FIONBIO, &nonblocking); + } + + static void socket_init() + { + WSAData wsa_data; + WORD wsa_version = MAKEWORD(2, 2); + + if(::WSAStartup(wsa_version, &wsa_data) != 0) + { + throw System_Error("WSAStartup() failed", WSAGetLastError()); + } + + if(LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) + { + ::WSACleanup(); + throw System_Error("Could not find a usable version of Winsock.dll"); + } + } + + static void socket_fini() + { + ::WSACleanup(); + } +#else + typedef int socket_type; + typedef ssize_t socket_op_ret_type; + typedef size_t sendrecv_len_type; + static socket_type invalid_socket() { return -1; } + static void close_socket(socket_type s) { ::close(s); } + static std::string get_last_socket_error() { return ::strerror(errno); } + static bool nonblocking_connect_in_progress() { return (errno == EINPROGRESS); } + static void set_nonblocking(socket_type s) + { + if(::fcntl(s, F_SETFL, O_NONBLOCK) < 0) + { throw System_Error("Setting socket to non-blocking state failed", errno); } + } + + static void socket_init() {} + static void socket_fini() {} +#endif + sockaddr_storage sa; + socklen_t salen; + struct timeval make_timeout_tv() const + { + struct timeval tv; + tv.tv_sec = static_cast<decltype(timeval::tv_sec)>(m_timeout.count() / 1000000); + tv.tv_usec = static_cast<decltype(timeval::tv_usec)>(m_timeout.count() % 1000000);; + return tv; + } + + const std::chrono::microseconds m_timeout; + socket_type m_socket; + }; +#endif +} + +std::unique_ptr<OS::SocketUDP> +OS::open_socket_udp(const std::string& hostname, + const std::string& service, + std::chrono::microseconds timeout) + { +#if defined(BOTAN_HAS_BOOST_ASIO) + return std::unique_ptr<OS::SocketUDP>(new Asio_SocketUDP(hostname, service, timeout)); +#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2) + return std::unique_ptr<OS::SocketUDP>(new BSD_SocketUDP(hostname, service, timeout)); +#else + BOTAN_UNUSED(hostname); + BOTAN_UNUSED(service); + BOTAN_UNUSED(timeout); + return std::unique_ptr<OS::SocketUDP>(); +#endif + } + +std::unique_ptr<OS::SocketUDP> +OS::open_socket_udp(const std::string& uri_string, + std::chrono::microseconds timeout) + { + const auto uri = URI::fromAny(uri_string); + if(uri.port == 0) + { throw Invalid_Argument("UDP port not specified"); } + return open_socket_udp(uri.host, std::to_string(uri.port), timeout); + } + +} diff --git a/comm/third_party/botan/src/lib/utils/socket/socket_udp.h b/comm/third_party/botan/src/lib/utils/socket/socket_udp.h new file mode 100644 index 0000000000..4a9346c2b3 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/socket/socket_udp.h @@ -0,0 +1,73 @@ +/* +* (C) 2015,2016,2017 Jack Lloyd +* (C) 2019 Nuno Goncalves <nunojpg@gmail.com> +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SOCKET_UDP_H_ +#define BOTAN_SOCKET_UDP_H_ + +#include <botan/types.h> +#include <string> +#include <chrono> + +namespace Botan { + +namespace OS { + +/* +* This header is internal (not installed) and these functions are not +* intended to be called by applications. However they are given public +* visibility (using BOTAN_TEST_API macro) for the tests. This also probably +* allows them to be overridden by the application on ELF systems, but +* this hasn't been tested. +*/ + + +/** +* A wrapper around a simple blocking UDP socket +*/ +class BOTAN_TEST_API SocketUDP + { + public: + /** + * The socket will be closed upon destruction + */ + virtual ~SocketUDP() = default; + + /** + * Write to the socket. Returns immediately. + * Throws on error. + */ + virtual void write(const uint8_t buf[], size_t len) = 0; + + /** + * Reads up to len bytes, returns bytes written to buf. + * Returns 0 on EOF. Throws on error. + */ + virtual size_t read(uint8_t buf[], size_t len) = 0; + }; + +/** +* Open up a socket. Will throw on error. Returns null if sockets are +* not available on this platform. +*/ +std::unique_ptr<SocketUDP> +BOTAN_TEST_API open_socket_udp(const std::string& hostname, + const std::string& service, + std::chrono::microseconds timeout); + +/** +* Open up a socket. Will throw on error. Returns null if sockets are +* not available on this platform. +*/ +std::unique_ptr<SocketUDP> +BOTAN_TEST_API open_socket_udp(const std::string& uri, + std::chrono::microseconds timeout); + + +} // OS +} // Botan + +#endif diff --git a/comm/third_party/botan/src/lib/utils/socket/uri.cpp b/comm/third_party/botan/src/lib/utils/socket/uri.cpp new file mode 100644 index 0000000000..5653d97e73 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/socket/uri.cpp @@ -0,0 +1,188 @@ +/* +* (C) 2019 Nuno Goncalves <nunojpg@gmail.com> +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/uri.h> +#include <botan/exceptn.h> + +#include <regex> + +#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) + #include <arpa/inet.h> + #include <sys/socket.h> + #include <netinet/in.h> +#elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2) + #include <ws2tcpip.h> +#endif + +#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2) + +namespace { + +constexpr bool isdigit(char ch) + { + return ch >= '0' && ch <= '9'; + } + +bool isDomain(const std::string& domain) + { +#if defined(__GLIBCXX__) && (__GLIBCXX__ < 20160726) + // GCC 4.8 does not support regex + BOTAN_UNUSED(domain); + return true; +#else + std::regex re( + R"(^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$)"); + std::cmatch m; + return std::regex_match(domain.c_str(), m, re); +#endif + } + +bool isIPv4(const std::string& ip) + { + sockaddr_storage inaddr; + return !!inet_pton(AF_INET, ip.c_str(), &inaddr); + } + +bool isIPv6(const std::string& ip) + { + sockaddr_storage in6addr; + return !!inet_pton(AF_INET6, ip.c_str(), &in6addr); + } +} + +namespace Botan { + +URI URI::fromDomain(const std::string& uri) + { + unsigned port = 0; + const auto port_pos = uri.find(':'); + if(port_pos != std::string::npos) + { + for(char c : uri.substr(port_pos+1)) + { + if(!isdigit(c)) + { throw Invalid_Argument("invalid"); } + port = port*10 + c - '0'; + if(port > 65535) + { throw Invalid_Argument("invalid"); } + } + } + const auto domain = uri.substr(0, port_pos); + if(isIPv4(domain)) + { throw Invalid_Argument("invalid"); } + if(!isDomain(domain)) + { throw Invalid_Argument("invalid"); } + return {Type::Domain, domain, uint16_t(port)}; + } + +URI URI::fromIPv4(const std::string& uri) + { + unsigned port = 0; + const auto port_pos = uri.find(':'); + if(port_pos != std::string::npos) + { + for(char c : uri.substr(port_pos+1)) + { + if(!isdigit(c)) + { throw Invalid_Argument("invalid"); } + port = port*10 + c - '0'; + if(port > 65535) + { throw Invalid_Argument("invalid"); } + } + } + const auto ip = uri.substr(0, port_pos); + if(!isIPv4(ip)) + { throw Invalid_Argument("invalid"); } + return { Type::IPv4, ip, uint16_t(port) }; + } + +URI URI::fromIPv6(const std::string& uri) + { + unsigned port = 0; + const auto port_pos = uri.find(']'); + const bool with_braces = (port_pos != std::string::npos); + if((uri[0]=='[') != with_braces) + { throw Invalid_Argument("invalid"); } + + if(with_braces && (uri.size() > port_pos + 1)) + { + if(uri[port_pos+1]!=':') + { throw Invalid_Argument("invalid"); } + for(char c : uri.substr(port_pos+2)) + { + if(!isdigit(c)) + { throw Invalid_Argument("invalid"); } + port = port*10 + c - '0'; + if(port > 65535) + { throw Invalid_Argument("invalid"); } + } + } + const auto ip = uri.substr((with_braces ? 1 : 0), port_pos - with_braces); + if(!isIPv6(ip)) + { throw Invalid_Argument("invalid"); } + return { Type::IPv6, ip, uint16_t(port) }; + } + +URI URI::fromAny(const std::string& uri) + { + + bool colon_seen=false; + bool non_number=false; + if(uri[0]=='[') + { return fromIPv6(uri); } + for(auto c : uri) + { + if(c == ':') + { + if(colon_seen) //seen two ':' + { return fromIPv6(uri); } + colon_seen = true; + } + else if(!isdigit(c) && c != '.') + { + non_number=true; + } + } + if(!non_number) + { + if(isIPv4(uri.substr(0, uri.find(':')))) + { + return fromIPv4(uri); + } + } + return fromDomain(uri); + } + +std::string URI::to_string() const + { + if(type == Type::NotSet) + { + throw Invalid_Argument("not set"); + } + + if(port != 0) + { + if(type == Type::IPv6) + { return "[" + host + "]:" + std::to_string(port); } + return host + ":" + std::to_string(port); + } + return host; + } + +} + +#else + +namespace Botan { + +URI URI::fromDomain(const std::string&) {throw Not_Implemented("No socket support enabled in build");} +URI URI::fromIPv4(const std::string&) {throw Not_Implemented("No socket support enabled in build");} +URI URI::fromIPv6(const std::string&) {throw Not_Implemented("No socket support enabled in build");} +URI URI::fromAny(const std::string&) {throw Not_Implemented("No socket support enabled in build");} + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/socket/uri.h b/comm/third_party/botan/src/lib/utils/socket/uri.h new file mode 100644 index 0000000000..346f9f4f63 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/socket/uri.h @@ -0,0 +1,49 @@ +/* +* (C) 2019 Nuno Goncalves <nunojpg@gmail.com> +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_URI_H_ +#define BOTAN_URI_H_ + +#include <cstdint> +#include <string> + +#include <botan/build.h> + +namespace Botan { + +struct BOTAN_TEST_API URI + { + enum class Type : uint8_t + { + NotSet, + IPv4, + IPv6, + Domain, + }; + static URI fromAny(const std::string& uri); + static URI fromIPv4(const std::string& uri); + static URI fromIPv6(const std::string& uri); + static URI fromDomain(const std::string& uri); + URI() = default; + URI(Type xtype, const std::string& xhost, unsigned short xport) + : type { xtype } + , host { xhost } + , port { xport } + {} + bool operator==(const URI& a) const + { + return type == a.type && host == a.host && port == a.port; + } + std::string to_string() const; + + const Type type{Type::NotSet}; + const std::string host{}; + const uint16_t port{}; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/sqlite3/info.txt b/comm/third_party/botan/src/lib/utils/sqlite3/info.txt new file mode 100644 index 0000000000..9611944323 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/sqlite3/info.txt @@ -0,0 +1,13 @@ +<defines> +SQLITE3 -> 20171118 +</defines> + +load_on vendor + +<libs> +all -> sqlite3 +</libs> + +<header:public> +sqlite3.h +</header:public> diff --git a/comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.cpp b/comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.cpp new file mode 100644 index 0000000000..8c71ecd964 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.cpp @@ -0,0 +1,166 @@ +/* +* SQLite wrapper +* (C) 2012 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/sqlite3.h> +#include <botan/exceptn.h> +#include <botan/mem_ops.h> +#include <sqlite3.h> + +namespace Botan { + +Sqlite3_Database::Sqlite3_Database(const std::string& db_filename) + { + int rc = ::sqlite3_open(db_filename.c_str(), &m_db); + + if(rc) + { + const std::string err_msg = ::sqlite3_errmsg(m_db); + ::sqlite3_close(m_db); + m_db = nullptr; + throw SQL_DB_Error("sqlite3_open failed - " + err_msg); + } + } + +Sqlite3_Database::~Sqlite3_Database() + { + if(m_db) + ::sqlite3_close(m_db); + m_db = nullptr; + } + +std::shared_ptr<SQL_Database::Statement> Sqlite3_Database::new_statement(const std::string& base_sql) const + { + return std::make_shared<Sqlite3_Statement>(m_db, base_sql); + } + +size_t Sqlite3_Database::row_count(const std::string& table_name) + { + auto stmt = new_statement("select count(*) from " + table_name); + + if(stmt->step()) + return stmt->get_size_t(0); + else + throw SQL_DB_Error("Querying size of table " + table_name + " failed"); + } + +void Sqlite3_Database::create_table(const std::string& table_schema) + { + char* errmsg = nullptr; + int rc = ::sqlite3_exec(m_db, table_schema.c_str(), nullptr, nullptr, &errmsg); + + if(rc != SQLITE_OK) + { + const std::string err_msg = errmsg; + ::sqlite3_free(errmsg); + ::sqlite3_close(m_db); + m_db = nullptr; + throw SQL_DB_Error("sqlite3_exec for table failed - " + err_msg); + } + } + +Sqlite3_Database::Sqlite3_Statement::Sqlite3_Statement(sqlite3* db, const std::string& base_sql) + { + int rc = ::sqlite3_prepare_v2(db, base_sql.c_str(), -1, &m_stmt, nullptr); + + if(rc != SQLITE_OK) + throw SQL_DB_Error("sqlite3_prepare failed on " + base_sql, rc); + } + +void Sqlite3_Database::Sqlite3_Statement::bind(int column, const std::string& val) + { + int rc = ::sqlite3_bind_text(m_stmt, column, val.c_str(), -1, SQLITE_TRANSIENT); + if(rc != SQLITE_OK) + throw SQL_DB_Error("sqlite3_bind_text failed", rc); + } + +void Sqlite3_Database::Sqlite3_Statement::bind(int column, size_t val) + { + if(val != static_cast<size_t>(static_cast<int>(val))) // is this cast legit? + throw SQL_DB_Error("sqlite3 cannot store " + std::to_string(val) + " without truncation"); + int rc = ::sqlite3_bind_int(m_stmt, column, val); + if(rc != SQLITE_OK) + throw SQL_DB_Error("sqlite3_bind_int failed", rc); + } + +void Sqlite3_Database::Sqlite3_Statement::bind(int column, std::chrono::system_clock::time_point time) + { + const int timeval = std::chrono::duration_cast<std::chrono::seconds>(time.time_since_epoch()).count(); + bind(column, timeval); + } + +void Sqlite3_Database::Sqlite3_Statement::bind(int column, const std::vector<uint8_t>& val) + { + int rc = ::sqlite3_bind_blob(m_stmt, column, val.data(), val.size(), SQLITE_TRANSIENT); + if(rc != SQLITE_OK) + throw SQL_DB_Error("sqlite3_bind_text failed", rc); + } + +void Sqlite3_Database::Sqlite3_Statement::bind(int column, const uint8_t* p, size_t len) + { + int rc = ::sqlite3_bind_blob(m_stmt, column, p, len, SQLITE_TRANSIENT); + if(rc != SQLITE_OK) + throw SQL_DB_Error("sqlite3_bind_text failed", rc); + } + +std::pair<const uint8_t*, size_t> Sqlite3_Database::Sqlite3_Statement::get_blob(int column) + { + BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_BLOB, + "Return value is a blob"); + + const void* session_blob = ::sqlite3_column_blob(m_stmt, column); + const int session_blob_size = ::sqlite3_column_bytes(m_stmt, column); + + BOTAN_ASSERT(session_blob_size >= 0, "Blob size is non-negative"); + + return std::make_pair(static_cast<const uint8_t*>(session_blob), + static_cast<size_t>(session_blob_size)); + } + +std::string Sqlite3_Database::Sqlite3_Statement::get_str(int column) + { + BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_TEXT, + "Return value is text"); + + const unsigned char* str = ::sqlite3_column_text(m_stmt, column); + + return std::string(cast_uint8_ptr_to_char(str)); + } + +size_t Sqlite3_Database::Sqlite3_Statement::get_size_t(int column) + { + BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_INTEGER, + "Return count is an integer"); + + const int sessions_int = ::sqlite3_column_int(m_stmt, column); + + BOTAN_ASSERT(sessions_int >= 0, "Expected size_t is non-negative"); + + return static_cast<size_t>(sessions_int); + } + +size_t Sqlite3_Database::Sqlite3_Statement::spin() + { + size_t steps = 0; + while(step()) + { + ++steps; + } + + return steps; + } + +bool Sqlite3_Database::Sqlite3_Statement::step() + { + return (::sqlite3_step(m_stmt) == SQLITE_ROW); + } + +Sqlite3_Database::Sqlite3_Statement::~Sqlite3_Statement() + { + ::sqlite3_finalize(m_stmt); + } + +} diff --git a/comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.h b/comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.h new file mode 100644 index 0000000000..08a0f0ae7c --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.h @@ -0,0 +1,58 @@ +/* +* SQLite3 wrapper +* (C) 2012,2014 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_UTILS_SQLITE3_H_ +#define BOTAN_UTILS_SQLITE3_H_ + +#include <botan/database.h> + +class sqlite3; +class sqlite3_stmt; + +namespace Botan { + +class BOTAN_PUBLIC_API(2,0) Sqlite3_Database final : public SQL_Database + { + public: + Sqlite3_Database(const std::string& file); + + ~Sqlite3_Database(); + + size_t row_count(const std::string& table_name) override; + + void create_table(const std::string& table_schema) override; + + std::shared_ptr<Statement> new_statement(const std::string& sql) const override; + private: + class Sqlite3_Statement final : public Statement + { + public: + void bind(int column, const std::string& val) override; + void bind(int column, size_t val) override; + void bind(int column, std::chrono::system_clock::time_point time) override; + void bind(int column, const std::vector<uint8_t>& val) override; + void bind(int column, const uint8_t* data, size_t len) override; + + std::pair<const uint8_t*, size_t> get_blob(int column) override; + std::string get_str(int column) override; + size_t get_size_t(int column) override; + + size_t spin() override; + bool step() override; + + Sqlite3_Statement(sqlite3* db, const std::string& base_sql); + ~Sqlite3_Statement(); + private: + sqlite3_stmt* m_stmt; + }; + + sqlite3* m_db; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/stl_compatibility.h b/comm/third_party/botan/src/lib/utils/stl_compatibility.h new file mode 100644 index 0000000000..03bd5c8ac1 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/stl_compatibility.h @@ -0,0 +1,80 @@ +/* +* STL standards compatibility functions +* (C) 2017 Tomasz Frydrych +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_STL_COMPATIBILITY_H_ +#define BOTAN_STL_COMPATIBILITY_H_ + +#include <botan/types.h> +#include <memory> + +#if __cplusplus < 201402L +#include <cstddef> +#include <type_traits> +#include <utility> +#endif + +BOTAN_FUTURE_INTERNAL_HEADER(stl_compatability.h) + +namespace Botan +{ +/* +* std::make_unique functionality similar as we have in C++14. +* C++11 version based on proposal for C++14 implemenatation by Stephan T. Lavavej +* source: https://isocpp.org/files/papers/N3656.txt +*/ +#if __cplusplus >= 201402L +template <typename T, typename ... Args> +constexpr auto make_unique(Args&&... args) + { + return std::make_unique<T>(std::forward<Args>(args)...); + } + +template<class T> +constexpr auto make_unique(std::size_t size) + { + return std::make_unique<T>(size); + } + +#else +namespace stlCompatibilityDetails +{ +template<class T> struct _Unique_if + { + typedef std::unique_ptr<T> _Single_object; + }; + +template<class T> struct _Unique_if<T[]> + { + typedef std::unique_ptr<T[]> _Unknown_bound; + }; + +template<class T, size_t N> struct _Unique_if<T[N]> + { + typedef void _Known_bound; + }; +} // namespace stlCompatibilityDetails + +template<class T, class... Args> +typename stlCompatibilityDetails::_Unique_if<T>::_Single_object make_unique(Args&&... args) + { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); + } + +template<class T> +typename stlCompatibilityDetails::_Unique_if<T>::_Unknown_bound make_unique(size_t n) + { + typedef typename std::remove_extent<T>::type U; + return std::unique_ptr<T>(new U[n]()); + } + +template<class T, class... Args> +typename stlCompatibilityDetails::_Unique_if<T>::_Known_bound make_unique(Args&&...) = delete; + +#endif + +} // namespace Botan +#endif diff --git a/comm/third_party/botan/src/lib/utils/stl_util.h b/comm/third_party/botan/src/lib/utils/stl_util.h new file mode 100644 index 0000000000..d9167bb7db --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/stl_util.h @@ -0,0 +1,110 @@ +/* +* STL Utility Functions +* (C) 1999-2007 Jack Lloyd +* (C) 2015 Simon Warta (Kullo GmbH) +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_STL_UTIL_H_ +#define BOTAN_STL_UTIL_H_ + +#include <vector> +#include <string> +#include <map> +#include <set> +#include <botan/secmem.h> + +namespace Botan { + +inline std::vector<uint8_t> to_byte_vector(const std::string& s) + { + return std::vector<uint8_t>(s.cbegin(), s.cend()); + } + +inline std::string to_string(const secure_vector<uint8_t> &bytes) + { + return std::string(bytes.cbegin(), bytes.cend()); + } + +/** +* Return the keys of a map as a std::set +*/ +template<typename K, typename V> +std::set<K> map_keys_as_set(const std::map<K, V>& kv) + { + std::set<K> s; + for(auto&& i : kv) + { + s.insert(i.first); + } + return s; + } + +/* +* Searching through a std::map +* @param mapping the map to search +* @param key is what to look for +* @param null_result is the value to return if key is not in mapping +* @return mapping[key] or null_result +*/ +template<typename K, typename V> +inline V search_map(const std::map<K, V>& mapping, + const K& key, + const V& null_result = V()) + { + auto i = mapping.find(key); + if(i == mapping.end()) + return null_result; + return i->second; + } + +template<typename K, typename V, typename R> +inline R search_map(const std::map<K, V>& mapping, const K& key, + const R& null_result, const R& found_result) + { + auto i = mapping.find(key); + if(i == mapping.end()) + return null_result; + return found_result; + } + +/* +* Insert a key/value pair into a multimap +*/ +template<typename K, typename V> +void multimap_insert(std::multimap<K, V>& multimap, + const K& key, const V& value) + { + multimap.insert(std::make_pair(key, value)); + } + +/** +* Existence check for values +*/ +template<typename T> +bool value_exists(const std::vector<T>& vec, + const T& val) + { + for(size_t i = 0; i != vec.size(); ++i) + if(vec[i] == val) + return true; + return false; + } + +template<typename T, typename Pred> +void map_remove_if(Pred pred, T& assoc) + { + auto i = assoc.begin(); + while(i != assoc.end()) + { + if(pred(i->first)) + assoc.erase(i++); + else + i++; + } + } + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/barrier.cpp b/comm/third_party/botan/src/lib/utils/thread_utils/barrier.cpp new file mode 100644 index 0000000000..c05c22121e --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/thread_utils/barrier.cpp @@ -0,0 +1,36 @@ +/* +* Barrier +* (C) 2016 Joel Low +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/barrier.h> + +namespace Botan { + +void Barrier::wait(size_t delta) + { + std::lock_guard<std::mutex> lock(m_mutex); + m_value += delta; + } + +void Barrier::sync() + { + std::unique_lock<std::mutex> lock(m_mutex); + + if(m_value > 1) + { + --m_value; + const size_t current_syncs = m_syncs; + m_cond.wait(lock, [this, ¤t_syncs] { return m_syncs != current_syncs; }); + } + else + { + m_value = 0; + ++m_syncs; + m_cond.notify_all(); + } + } + +} diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/barrier.h b/comm/third_party/botan/src/lib/utils/thread_utils/barrier.h new file mode 100644 index 0000000000..749da92a77 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/thread_utils/barrier.h @@ -0,0 +1,42 @@ +/* +* Barrier +* (C) 2016 Joel Low +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_UTIL_BARRIER_H_ +#define BOTAN_UTIL_BARRIER_H_ + +#include <mutex> +#include <condition_variable> + +namespace Botan { + +/** +Barrier implements a barrier synchronization primitive. wait() will +indicate how many threads to synchronize; each thread needing +synchronization should call sync(). When sync() returns, the barrier +is reset to zero, and the m_syncs counter is incremented. m_syncs is a +counter to ensure that wait() can be called after a sync() even if the +previously sleeping threads have not awoken.) +*/ +class Barrier final + { + public: + explicit Barrier(int value = 0) : m_value(value), m_syncs(0) {} + + void wait(size_t delta); + + void sync(); + + private: + size_t m_value; + size_t m_syncs; + std::mutex m_mutex; + std::condition_variable m_cond; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/info.txt b/comm/third_party/botan/src/lib/utils/thread_utils/info.txt new file mode 100644 index 0000000000..057f536005 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/thread_utils/info.txt @@ -0,0 +1,14 @@ +<defines> +THREAD_UTILS -> 20190922 +</defines> + +<header:internal> +rwlock.h +barrier.h +semaphore.h +thread_pool.h +</header:internal> + +<os_features> +threads +</os_features> diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/rwlock.cpp b/comm/third_party/botan/src/lib/utils/thread_utils/rwlock.cpp new file mode 100644 index 0000000000..58500a83de --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/thread_utils/rwlock.cpp @@ -0,0 +1,58 @@ +/* +* (C) 2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/rwlock.h> + +namespace Botan { + +RWLock::RWLock() : m_state(0) {} + +void RWLock::lock() + { + std::unique_lock<std::mutex> lock(m_mutex); + while(m_state & is_writing) + m_gate1.wait(lock); + m_state |= is_writing; + while(m_state & readers_mask) + m_gate2.wait(lock); + } + +void RWLock::unlock() + { + std::unique_lock<std::mutex> lock(m_mutex); + m_state = 0; + m_gate1.notify_all(); + } + +void RWLock::lock_shared() + { + std::unique_lock<std::mutex> lock(m_mutex); + while((m_state & is_writing) || (m_state & readers_mask) == readers_mask) + m_gate1.wait(lock); + const uint32_t num_readers = (m_state & readers_mask) + 1; + m_state &= ~readers_mask; + m_state |= num_readers; + } + +void RWLock::unlock_shared() + { + std::unique_lock<std::mutex> lock(m_mutex); + const uint32_t num_readers = (m_state & readers_mask) - 1; + m_state &= ~readers_mask; + m_state |= num_readers; + if(m_state & is_writing) + { + if(num_readers == 0) + m_gate2.notify_one(); + } + else + { + if(num_readers == readers_mask - 1) + m_gate1.notify_one(); + } + } + +} diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/rwlock.h b/comm/third_party/botan/src/lib/utils/thread_utils/rwlock.h new file mode 100644 index 0000000000..324e667538 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/thread_utils/rwlock.h @@ -0,0 +1,42 @@ +/* +* (C) 2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_RWLOCK_H_ +#define BOTAN_RWLOCK_H_ + +#include <botan/types.h> +#include <mutex> +#include <condition_variable> + +namespace Botan { + +/** +* A read-write lock. Writers are favored. +*/ +class BOTAN_TEST_API RWLock final + { + public: + RWLock(); + + void lock(); + void unlock(); + + void lock_shared(); + void unlock_shared(); + private: + std::mutex m_mutex; + std::condition_variable m_gate1; + std::condition_variable m_gate2; + uint32_t m_state; + + // 2**31 concurrent readers should be enough for anyone + static const uint32_t is_writing = static_cast<uint32_t>(1) << 31; + static const uint32_t readers_mask = ~is_writing; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/semaphore.cpp b/comm/third_party/botan/src/lib/utils/thread_utils/semaphore.cpp new file mode 100644 index 0000000000..0858a013f2 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/thread_utils/semaphore.cpp @@ -0,0 +1,38 @@ +/* +* Semaphore +* (C) 2013 Joel Low +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/semaphore.h> + +// Based on code by Pierre Gaston (http://p9as.blogspot.com/2012/06/c11-semaphores.html) + +namespace Botan { + +void Semaphore::release(size_t n) + { + for(size_t i = 0; i != n; ++i) + { + std::lock_guard<std::mutex> lock(m_mutex); + + if(m_value++ < 0) + { + ++m_wakeups; + m_cond.notify_one(); + } + } + } + +void Semaphore::acquire() + { + std::unique_lock<std::mutex> lock(m_mutex); + if(m_value-- <= 0) + { + m_cond.wait(lock, [this] { return m_wakeups > 0; }); + --m_wakeups; + } + } + +} diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/semaphore.h b/comm/third_party/botan/src/lib/utils/thread_utils/semaphore.h new file mode 100644 index 0000000000..538d9ecb51 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/thread_utils/semaphore.h @@ -0,0 +1,34 @@ +/* +* Semaphore +* (C) 2013 Joel Low +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SEMAPHORE_H_ +#define BOTAN_SEMAPHORE_H_ + +#include <condition_variable> +#include <mutex> + +namespace Botan { + +class Semaphore final + { + public: + explicit Semaphore(int value = 0) : m_value(value), m_wakeups(0) {} + + void acquire(); + + void release(size_t n = 1); + + private: + int m_value; + int m_wakeups; + std::mutex m_mutex; + std::condition_variable m_cond; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.cpp b/comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.cpp new file mode 100644 index 0000000000..405f79585a --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.cpp @@ -0,0 +1,103 @@ +/* +* (C) 2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/thread_pool.h> +#include <botan/internal/os_utils.h> +#include <botan/exceptn.h> +#include <thread> + +namespace Botan { + +//static +Thread_Pool& Thread_Pool::global_instance() + { + static Thread_Pool g_thread_pool(OS::read_env_variable_sz("BOTAN_THREAD_POOL_SIZE")); + return g_thread_pool; + } + +Thread_Pool::Thread_Pool(size_t pool_size) + { + if(pool_size == 0) + { + pool_size = OS::get_cpu_available(); + + /* + * For large machines don't create too many threads, unless + * explicitly asked to by the caller. + */ + if(pool_size > 16) + pool_size = 16; + } + + if(pool_size <= 1) + pool_size = 2; + + m_shutdown = false; + + for(size_t i = 0; i != pool_size; ++i) + { + m_workers.push_back(std::thread(&Thread_Pool::worker_thread, this)); + } + } + +void Thread_Pool::shutdown() + { + { + std::unique_lock<std::mutex> lock(m_mutex); + + if(m_shutdown == true) + return; + + m_shutdown = true; + + m_more_tasks.notify_all(); + } + + for(auto&& thread : m_workers) + { + thread.join(); + } + m_workers.clear(); + } + +void Thread_Pool::queue_thunk(std::function<void ()> fn) + { + std::unique_lock<std::mutex> lock(m_mutex); + + if(m_shutdown) + throw Invalid_State("Cannot add work after thread pool has shut down"); + + m_tasks.push_back(fn); + m_more_tasks.notify_one(); + } + +void Thread_Pool::worker_thread() + { + for(;;) + { + std::function<void()> task; + + { + std::unique_lock<std::mutex> lock(m_mutex); + m_more_tasks.wait(lock, [this]{ return m_shutdown || !m_tasks.empty(); }); + + if(m_tasks.empty()) + { + if(m_shutdown) + return; + else + continue; + } + + task = m_tasks.front(); + m_tasks.pop_front(); + } + + task(); + } + } + +} diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.h b/comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.h new file mode 100644 index 0000000000..34f4f26dbc --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.h @@ -0,0 +1,82 @@ +/* +* (C) 2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_THREAD_POOL_H_ +#define BOTAN_THREAD_POOL_H_ + +#include <botan/types.h> +#include <functional> +#include <deque> +#include <vector> +#include <memory> +#include <utility> +#include <type_traits> +#include <mutex> +#include <thread> +#include <future> +#include <condition_variable> + +namespace Botan { + +class BOTAN_TEST_API Thread_Pool + { + public: + /** + * Return an instance to a shared thread pool + */ + static Thread_Pool& global_instance(); + + /** + * Initialize a thread pool with some number of threads + * @param pool_size number of threads in the pool, if 0 + * then some default value is chosen + */ + Thread_Pool(size_t pool_size = 0); + + ~Thread_Pool() { shutdown(); } + + void shutdown(); + + size_t worker_count() const { return m_workers.size(); } + + Thread_Pool(const Thread_Pool&) = delete; + Thread_Pool& operator=(const Thread_Pool&) = delete; + + Thread_Pool(Thread_Pool&&) = delete; + Thread_Pool& operator=(Thread_Pool&&) = delete; + + /* + * Enqueue some work + */ + void queue_thunk(std::function<void ()>); + + template<class F, class... Args> + auto run(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> + { + typedef typename std::result_of<F(Args...)>::type return_type; + + auto future_work = std::bind(std::forward<F>(f), std::forward<Args>(args)...); + auto task = std::make_shared<std::packaged_task<return_type ()>>(future_work); + auto future_result = task->get_future(); + queue_thunk([task]() { (*task)(); }); + return future_result; + } + + private: + void worker_thread(); + + // Only touched in constructor and destructor + std::vector<std::thread> m_workers; + + std::mutex m_mutex; + std::condition_variable m_more_tasks; + std::deque<std::function<void ()>> m_tasks; + bool m_shutdown; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/timer.cpp b/comm/third_party/botan/src/lib/utils/timer.cpp new file mode 100644 index 0000000000..404da5ce8b --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/timer.cpp @@ -0,0 +1,150 @@ +/* +* (C) 2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/timer.h> +#include <botan/internal/os_utils.h> +#include <algorithm> +#include <sstream> +#include <iomanip> + +namespace Botan { + +void Timer::start() + { + stop(); + m_timer_start = OS::get_system_timestamp_ns(); + m_cpu_cycles_start = OS::get_cpu_cycle_counter(); + } + +void Timer::stop() + { + if(m_timer_start) + { + if(m_cpu_cycles_start != 0) + { + const uint64_t cycles_taken = OS::get_cpu_cycle_counter() - m_cpu_cycles_start; + if(cycles_taken > 0) + { + m_cpu_cycles_used += static_cast<size_t>(cycles_taken * m_clock_cycle_ratio); + } + } + + const uint64_t now = OS::get_system_timestamp_ns(); + + if(now > m_timer_start) + { + const uint64_t dur = now - m_timer_start; + + m_time_used += dur; + + if(m_event_count == 0) + { + m_min_time = m_max_time = dur; + } + else + { + m_max_time = std::max(m_max_time, dur); + m_min_time = std::min(m_min_time, dur); + } + } + + m_timer_start = 0; + ++m_event_count; + } + } + +bool Timer::operator<(const Timer& other) const + { + if(this->doing() != other.doing()) + return (this->doing() < other.doing()); + + return (this->get_name() < other.get_name()); + } + +std::string Timer::to_string() const + { + if(m_custom_msg.size() > 0) + { + return m_custom_msg; + } + else if(this->buf_size() == 0) + { + return result_string_ops(); + } + else + { + return result_string_bps(); + } + } + +std::string Timer::result_string_bps() const + { + const size_t MiB = 1024 * 1024; + + const double MiB_total = static_cast<double>(events()) / MiB; + const double MiB_per_sec = MiB_total / seconds(); + + std::ostringstream oss; + oss << get_name(); + + if(!doing().empty()) + { + oss << " " << doing(); + } + + if(buf_size() > 0) + { + oss << " buffer size " << buf_size() << " bytes:"; + } + + if(events() == 0) + oss << " " << "N/A"; + else + oss << " " << std::fixed << std::setprecision(3) << MiB_per_sec << " MiB/sec"; + + if(cycles_consumed() != 0) + { + const double cycles_per_byte = static_cast<double>(cycles_consumed()) / events(); + oss << " " << std::fixed << std::setprecision(2) << cycles_per_byte << " cycles/byte"; + } + + oss << " (" << MiB_total << " MiB in " << milliseconds() << " ms)\n"; + + return oss.str(); + } + +std::string Timer::result_string_ops() const + { + std::ostringstream oss; + + oss << get_name() << " "; + + if(events() == 0) + { + oss << "no events\n"; + } + else + { + oss << static_cast<uint64_t>(events_per_second()) + << ' ' << doing() << "/sec; " + << std::setprecision(2) << std::fixed + << ms_per_event() << " ms/op"; + + if(cycles_consumed() != 0) + { + const double cycles_per_op = static_cast<double>(cycles_consumed()) / events(); + const int precision = (cycles_per_op < 10000) ? 2 : 0; + oss << " " << std::fixed << std::setprecision(precision) << cycles_per_op << " cycles/op"; + } + + oss << " (" << events() << " " << (events() == 1 ? "op" : "ops") + << " in " << milliseconds() << " ms)\n"; + } + + return oss.str(); + } + +} diff --git a/comm/third_party/botan/src/lib/utils/timer.h b/comm/third_party/botan/src/lib/utils/timer.h new file mode 100644 index 0000000000..f54c81fb62 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/timer.h @@ -0,0 +1,185 @@ +/* +* (C) 2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TIMER_H_ +#define BOTAN_TIMER_H_ + +#include <botan/types.h> +#include <string> +#include <chrono> + +namespace Botan { + +class BOTAN_TEST_API Timer final + { + public: + Timer(const std::string& name, + const std::string& provider, + const std::string& doing, + uint64_t event_mult, + size_t buf_size, + double clock_cycle_ratio, + uint64_t clock_speed) + : m_name(name + ((provider.empty() || provider == "base") ? "" : " [" + provider + "]")) + , m_doing(doing) + , m_buf_size(buf_size) + , m_event_mult(event_mult) + , m_clock_cycle_ratio(clock_cycle_ratio) + , m_clock_speed(clock_speed) + {} + + Timer(const std::string& name) : + Timer(name, "", "", 1, 0, 0.0, 0) + {} + + Timer(const std::string& name, size_t buf_size) : + Timer(name, "", "", buf_size, buf_size, 0.0, 0) + {} + + Timer(const Timer& other) = default; + Timer& operator=(const Timer& other) = default; + + void start(); + + void stop(); + + bool under(std::chrono::milliseconds msec) + { + return (milliseconds() < msec.count()); + } + + class Timer_Scope final + { + public: + explicit Timer_Scope(Timer& timer) + : m_timer(timer) + { + m_timer.start(); + } + ~Timer_Scope() + { + try + { + m_timer.stop(); + } + catch(...) {} + } + private: + Timer& m_timer; + }; + + template<typename F> + auto run(F f) -> decltype(f()) + { + Timer_Scope timer(*this); + return f(); + } + + template<typename F> + void run_until_elapsed(std::chrono::milliseconds msec, F f) + { + while(this->under(msec)) + { + run(f); + } + } + + uint64_t value() const + { + return m_time_used; + } + + double seconds() const + { + return milliseconds() / 1000.0; + } + + double milliseconds() const + { + return value() / 1000000.0; + } + + double ms_per_event() const + { + return milliseconds() / events(); + } + + uint64_t cycles_consumed() const + { + if(m_clock_speed != 0) + { + return static_cast<uint64_t>((m_clock_speed * value()) / 1000.0); + } + return m_cpu_cycles_used; + } + + uint64_t events() const + { + return m_event_count * m_event_mult; + } + + const std::string& get_name() const + { + return m_name; + } + + const std::string& doing() const + { + return m_doing; + } + + size_t buf_size() const + { + return m_buf_size; + } + + double bytes_per_second() const + { + return seconds() > 0.0 ? events() / seconds() : 0.0; + } + + double events_per_second() const + { + return seconds() > 0.0 ? events() / seconds() : 0.0; + } + + double seconds_per_event() const + { + return events() > 0 ? seconds() / events() : 0.0; + } + + void set_custom_msg(const std::string& s) + { + m_custom_msg = s; + } + + bool operator<(const Timer& other) const; + + std::string to_string() const; + + private: + std::string result_string_bps() const; + std::string result_string_ops() const; + + // const data + std::string m_name, m_doing; + size_t m_buf_size; + uint64_t m_event_mult; + double m_clock_cycle_ratio; + uint64_t m_clock_speed; + + // set at runtime + std::string m_custom_msg; + uint64_t m_time_used = 0, m_timer_start = 0; + uint64_t m_event_count = 0; + + uint64_t m_max_time = 0, m_min_time = 0; + uint64_t m_cpu_cycles_start = 0, m_cpu_cycles_used = 0; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/types.h b/comm/third_party/botan/src/lib/utils/types.h new file mode 100644 index 0000000000..549163aa8c --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/types.h @@ -0,0 +1,112 @@ +/* +* Low Level Types +* (C) 1999-2007 Jack Lloyd +* (C) 2015 Simon Warta (Kullo GmbH) +* (C) 2016 RenĂ© Korthaus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TYPES_H_ +#define BOTAN_TYPES_H_ + +#include <botan/build.h> // IWYU pragma: export +#include <botan/assert.h> // IWYU pragma: export +#include <cstddef> // IWYU pragma: export +#include <cstdint> // IWYU pragma: export +#include <memory> // IWYU pragma: export + +namespace Botan { + +/** +* @mainpage Botan Crypto Library API Reference +* +* <dl> +* <dt>Abstract Base Classes<dd> +* BlockCipher, HashFunction, KDF, MessageAuthenticationCode, RandomNumberGenerator, +* StreamCipher, SymmetricAlgorithm, AEAD_Mode, Cipher_Mode +* <dt>Public Key Interface Classes<dd> +* PK_Key_Agreement, PK_Signer, PK_Verifier, PK_Encryptor, PK_Decryptor +* <dt>Authenticated Encryption Modes<dd> +* @ref CCM_Mode "CCM", @ref ChaCha20Poly1305_Mode "ChaCha20Poly1305", @ref EAX_Mode "EAX", +* @ref GCM_Mode "GCM", @ref OCB_Mode "OCB", @ref SIV_Mode "SIV" +* <dt>Block Ciphers<dd> +* @ref aria.h "ARIA", @ref aes.h "AES", @ref Blowfish, @ref camellia.h "Camellia", @ref Cascade_Cipher "Cascade", +* @ref CAST_128 "CAST-128", @ref CAST_128 "CAST-256", DES, @ref DESX "DES-X", @ref TripleDES "3DES", +* @ref GOST_28147_89 "GOST 28147-89", IDEA, KASUMI, Lion, MISTY1, Noekeon, SEED, Serpent, SHACAL2, SM4, +* @ref Threefish_512 "Threefish", Twofish, XTEA +* <dt>Stream Ciphers<dd> +* ChaCha, @ref CTR_BE "CTR", OFB, RC4, Salsa20 +* <dt>Hash Functions<dd> +* BLAKE2b, @ref GOST_34_11 "GOST 34.11", @ref Keccak_1600 "Keccak", MD4, MD5, @ref RIPEMD_160 "RIPEMD-160", +* @ref SHA_160 "SHA-1", @ref SHA_224 "SHA-224", @ref SHA_256 "SHA-256", @ref SHA_384 "SHA-384", +* @ref SHA_512 "SHA-512", @ref Skein_512 "Skein-512", SM3, Streebog, Tiger, Whirlpool +* <dt>Non-Cryptographic Checksums<dd> +* Adler32, CRC24, CRC32 +* <dt>Message Authentication Codes<dd> +* @ref CBC_MAC "CBC-MAC", CMAC, HMAC, Poly1305, SipHash, ANSI_X919_MAC +* <dt>Random Number Generators<dd> +* AutoSeeded_RNG, HMAC_DRBG, Processor_RNG, System_RNG +* <dt>Key Derivation<dd> +* HKDF, @ref KDF1 "KDF1 (IEEE 1363)", @ref KDF1_18033 "KDF1 (ISO 18033-2)", @ref KDF2 "KDF2 (IEEE 1363)", +* @ref sp800_108.h "SP800-108", @ref SP800_56C "SP800-56C", @ref PKCS5_PBKDF1 "PBKDF1 (PKCS#5), +* @ref PKCS5_PBKDF2 "PBKDF2 (PKCS#5)" +* <dt>Password Hashing<dd> +* @ref argon2.h "Argon2", @ref scrypt.h "scrypt", @ref bcrypt.h "bcrypt", @ref passhash9.h "passhash9" +* <dt>Public Key Cryptosystems<dd> +* @ref dlies.h "DLIES", @ref ecies.h "ECIES", @ref elgamal.h "ElGamal" +* @ref rsa.h "RSA", @ref newhope.h "NewHope", @ref mceliece.h "McEliece" and @ref mceies.h "MCEIES", +* @ref sm2.h "SM2" +* <dt>Public Key Signature Schemes<dd> +* @ref dsa.h "DSA", @ref ecdsa.h "ECDSA", @ref ecgdsa.h "ECGDSA", @ref eckcdsa.h "ECKCDSA", +* @ref gost_3410.h "GOST 34.10-2001", @ref sm2.h "SM2", @ref xmss.h "XMSS" +* <dt>Key Agreement<dd> +* @ref dh.h "DH", @ref ecdh.h "ECDH" +* <dt>Compression<dd> +* @ref bzip2.h "bzip2", @ref lzma.h "lzma", @ref zlib.h "zlib" +* <dt>TLS<dd> +* TLS::Client, TLS::Server, TLS::Policy, TLS::Protocol_Version, TLS::Callbacks, TLS::Ciphersuite, +* TLS::Session, TLS::Session_Manager, Credentials_Manager +* <dt>X.509<dd> +* X509_Certificate, X509_CRL, X509_CA, Certificate_Extension, PKCS10_Request, X509_Cert_Options, +* Certificate_Store, Certificate_Store_In_SQL, Certificate_Store_In_SQLite +* </dl> +*/ + +using std::uint8_t; +using std::uint16_t; +using std::uint32_t; +using std::uint64_t; +using std::int32_t; +using std::int64_t; +using std::size_t; + +/* +* These typedefs are no longer used within the library headers +* or code. They are kept only for compatability with software +* written against older versions. +*/ +using byte = std::uint8_t; +using u16bit = std::uint16_t; +using u32bit = std::uint32_t; +using u64bit = std::uint64_t; +using s32bit = std::int32_t; + +#if (BOTAN_MP_WORD_BITS == 32) + typedef uint32_t word; +#elif (BOTAN_MP_WORD_BITS == 64) + typedef uint64_t word; +#else + #error BOTAN_MP_WORD_BITS must be 32 or 64 +#endif + +/* +* Should this assert fail on your system please contact the developers +* for assistance in porting. +*/ +static_assert(sizeof(std::size_t) == 8 || sizeof(std::size_t) == 4, + "This platform has an unexpected size for size_t"); + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/uuid/info.txt b/comm/third_party/botan/src/lib/utils/uuid/info.txt new file mode 100644 index 0000000000..b078146e8a --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/uuid/info.txt @@ -0,0 +1,8 @@ +<defines> +UUID -> 20180930 +</defines> + +<requires> +rng +hex +</requires> diff --git a/comm/third_party/botan/src/lib/utils/uuid/uuid.cpp b/comm/third_party/botan/src/lib/utils/uuid/uuid.cpp new file mode 100644 index 0000000000..c81cae6657 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/uuid/uuid.cpp @@ -0,0 +1,82 @@ +/* +* UUID type +* (C) 2015,2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/uuid.h> +#include <botan/rng.h> +#include <botan/hex.h> +#include <sstream> + +namespace Botan { + +UUID::UUID(RandomNumberGenerator& rng) + { + m_uuid.resize(16); + rng.randomize(m_uuid.data(), m_uuid.size()); + + // Mark as a random v4 UUID (RFC 4122 sec 4.4) + m_uuid[6] = 0x40 | (m_uuid[6] & 0x0F); + + // Set reserved bits + m_uuid[8] = 0x80 | (m_uuid[8] & 0x3F); + } + +UUID::UUID(const std::vector<uint8_t>& blob) + { + if(blob.size() != 16) + { + throw Invalid_Argument("Bad UUID blob " + hex_encode(blob)); + } + + m_uuid = blob; + } + +UUID::UUID(const std::string& uuid_str) + { + if(uuid_str.size() != 36 || + uuid_str[8] != '-' || + uuid_str[13] != '-' || + uuid_str[18] != '-' || + uuid_str[23] != '-') + { + throw Invalid_Argument("Bad UUID '" + uuid_str + "'"); + } + + std::string just_hex; + for(size_t i = 0; i != uuid_str.size(); ++i) + { + char c = uuid_str[i]; + + if(c == '-') + continue; + + just_hex += c; + } + + m_uuid = hex_decode(just_hex); + + if(m_uuid.size() != 16) + { + throw Invalid_Argument("Bad UUID '" + uuid_str + "'"); + } + } + +std::string UUID::to_string() const + { + if(is_valid() == false) + throw Invalid_State("UUID object is empty cannot convert to string"); + + std::string h = hex_encode(m_uuid); + + h.insert(8, "-"); + h.insert(13, "-"); + h.insert(18, "-"); + h.insert(23, "-"); + + return h; + } + +} diff --git a/comm/third_party/botan/src/lib/utils/uuid/uuid.h b/comm/third_party/botan/src/lib/utils/uuid/uuid.h new file mode 100644 index 0000000000..8f95f4d672 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/uuid/uuid.h @@ -0,0 +1,69 @@ +/* +* UUID type +* (C) 2015,2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_UUID_H_ +#define BOTAN_UUID_H_ + +#include <botan/types.h> +#include <vector> +#include <string> + +BOTAN_FUTURE_INTERNAL_HEADER(uuid.h) + +namespace Botan { + +class RandomNumberGenerator; + +class BOTAN_UNSTABLE_API UUID final + { + public: + /** + * Create an uninitialized UUID object + */ + UUID() : m_uuid() {} + + /** + * Create a random UUID + */ + UUID(RandomNumberGenerator& rng); + + /** + * Load a UUID from a 16 byte vector + */ + UUID(const std::vector<uint8_t>& blob); + + UUID& operator=(const UUID& other) = default; + UUID(const UUID& other) = default; + + /** + * Decode a UUID string + */ + UUID(const std::string& uuid_str); + + /** + * Convert the UUID to a string + */ + std::string to_string() const; + + const std::vector<uint8_t>& binary_value() const { return m_uuid; } + + bool operator==(const UUID& other) const + { + return m_uuid == other.m_uuid; + } + + bool operator!=(const UUID& other) const { return !(*this == other); } + + bool is_valid() const { return m_uuid.size() == 16; } + + private: + std::vector<uint8_t> m_uuid; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/utils/version.cpp b/comm/third_party/botan/src/lib/utils/version.cpp new file mode 100644 index 0000000000..aa82d7cc95 --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/version.cpp @@ -0,0 +1,100 @@ +/* +* Version Information +* (C) 1999-2013,2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/version.h> +#include <sstream> + +namespace Botan { + +/* + These are intentionally compiled rather than inlined, so an + application running against a shared library can test the true + version they are running against. +*/ + +#define QUOTE(name) #name +#define STR(macro) QUOTE(macro) + +const char* short_version_cstr() + { + return STR(BOTAN_VERSION_MAJOR) "." + STR(BOTAN_VERSION_MINOR) "." + STR(BOTAN_VERSION_PATCH) +#if defined(BOTAN_VERSION_SUFFIX) + STR(BOTAN_VERSION_SUFFIX) +#endif + ; + } + +const char* version_cstr() + { + + /* + It is intentional that this string is a compile-time constant; + it makes it much easier to find in binaries. + */ + + return "Botan " STR(BOTAN_VERSION_MAJOR) "." + STR(BOTAN_VERSION_MINOR) "." + STR(BOTAN_VERSION_PATCH) +#if defined(BOTAN_VERSION_SUFFIX) + STR(BOTAN_VERSION_SUFFIX) +#endif + " (" +#if defined(BOTAN_UNSAFE_FUZZER_MODE) + "UNSAFE FUZZER MODE BUILD " +#endif + BOTAN_VERSION_RELEASE_TYPE +#if (BOTAN_VERSION_DATESTAMP != 0) + ", dated " STR(BOTAN_VERSION_DATESTAMP) +#endif + ", revision " BOTAN_VERSION_VC_REVISION + ", distribution " BOTAN_DISTRIBUTION_INFO ")"; + } + +#undef STR +#undef QUOTE + +/* +* Return the version as a string +*/ +std::string version_string() + { + return std::string(version_cstr()); + } + +std::string short_version_string() + { + return std::string(short_version_cstr()); + } + +uint32_t version_datestamp() { return BOTAN_VERSION_DATESTAMP; } + +/* +* Return parts of the version as integers +*/ +uint32_t version_major() { return BOTAN_VERSION_MAJOR; } +uint32_t version_minor() { return BOTAN_VERSION_MINOR; } +uint32_t version_patch() { return BOTAN_VERSION_PATCH; } + +std::string runtime_version_check(uint32_t major, + uint32_t minor, + uint32_t patch) + { + if(major != version_major() || minor != version_minor() || patch != version_patch()) + { + std::ostringstream oss; + oss << "Warning: linked version (" << short_version_string() << ")" + << " does not match version built against " + << "(" << major << '.' << minor << '.' << patch << ")\n"; + return oss.str(); + } + + return ""; + } + +} diff --git a/comm/third_party/botan/src/lib/utils/version.h b/comm/third_party/botan/src/lib/utils/version.h new file mode 100644 index 0000000000..fe59de625c --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/version.h @@ -0,0 +1,101 @@ +/* +* Version Information +* (C) 1999-2011,2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_VERSION_H_ +#define BOTAN_VERSION_H_ + +#include <botan/types.h> +#include <string> + +namespace Botan { + +/* +* Get information describing the version +*/ + +/** +* Get a human-readable string identifying the version of Botan. +* No particular format should be assumed. +* @return version string +*/ +BOTAN_PUBLIC_API(2,0) std::string version_string(); + +/** +* Same as version_string() except returning a pointer to a statically +* allocated string. +* @return version string +*/ +BOTAN_PUBLIC_API(2,0) const char* version_cstr(); + +/** +* Return a version string of the form "MAJOR.MINOR.PATCH" where +* each of the values is an integer. +*/ +BOTAN_PUBLIC_API(2,4) std::string short_version_string(); + +/** +* Same as version_short_string except returning a pointer to the string. +*/ +BOTAN_PUBLIC_API(2,4) const char* short_version_cstr(); + +/** +* Return the date this version of botan was released, in an integer of +* the form YYYYMMDD. For instance a version released on May 21, 2013 +* would return the integer 20130521. If the currently running version +* is not an official release, this function will return 0 instead. +* +* @return release date, or zero if unreleased +*/ +BOTAN_PUBLIC_API(2,0) uint32_t version_datestamp(); + +/** +* Get the major version number. +* @return major version number +*/ +BOTAN_PUBLIC_API(2,0) uint32_t version_major(); + +/** +* Get the minor version number. +* @return minor version number +*/ +BOTAN_PUBLIC_API(2,0) uint32_t version_minor(); + +/** +* Get the patch number. +* @return patch number +*/ +BOTAN_PUBLIC_API(2,0) uint32_t version_patch(); + +/** +* Usable for checking that the DLL version loaded at runtime exactly +* matches the compile-time version. Call using BOTAN_VERSION_* macro +* values. Returns the empty string if an exact match, otherwise an +* appropriate message. Added with 1.11.26. +*/ +BOTAN_PUBLIC_API(2,0) std::string +runtime_version_check(uint32_t major, + uint32_t minor, + uint32_t patch); + +/* +* Macros for compile-time version checks +*/ +#define BOTAN_VERSION_CODE_FOR(a,b,c) ((a << 16) | (b << 8) | (c)) + +/** +* Compare using BOTAN_VERSION_CODE_FOR, as in +* # if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,8,0) +* # error "Botan version too old" +* # endif +*/ +#define BOTAN_VERSION_CODE BOTAN_VERSION_CODE_FOR(BOTAN_VERSION_MAJOR, \ + BOTAN_VERSION_MINOR, \ + BOTAN_VERSION_PATCH) + +} + +#endif |