summaryrefslogtreecommitdiffstats
path: root/comm/third_party/botan/src/lib/utils
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/botan/src/lib/utils')
-rw-r--r--comm/third_party/botan/src/lib/utils/assert.cpp54
-rw-r--r--comm/third_party/botan/src/lib/utils/assert.h157
-rw-r--r--comm/third_party/botan/src/lib/utils/bit_ops.h171
-rw-r--r--comm/third_party/botan/src/lib/utils/boost/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/utils/bswap.h108
-rw-r--r--comm/third_party/botan/src/lib/utils/calendar.cpp124
-rw-r--r--comm/third_party/botan/src/lib/utils/calendar.h91
-rw-r--r--comm/third_party/botan/src/lib/utils/charset.cpp283
-rw-r--r--comm/third_party/botan/src/lib/utils/charset.h80
-rw-r--r--comm/third_party/botan/src/lib/utils/codec_base.h220
-rw-r--r--comm/third_party/botan/src/lib/utils/compiler.h225
-rw-r--r--comm/third_party/botan/src/lib/utils/cpuid/cpuid.cpp231
-rw-r--r--comm/third_party/botan/src/lib/utils/cpuid/cpuid.h484
-rw-r--r--comm/third_party/botan/src/lib/utils/cpuid/cpuid_arm.cpp237
-rw-r--r--comm/third_party/botan/src/lib/utils/cpuid/cpuid_ppc.cpp132
-rw-r--r--comm/third_party/botan/src/lib/utils/cpuid/cpuid_x86.cpp214
-rw-r--r--comm/third_party/botan/src/lib/utils/cpuid/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/utils/ct_utils.cpp83
-rw-r--r--comm/third_party/botan/src/lib/utils/ct_utils.h418
-rw-r--r--comm/third_party/botan/src/lib/utils/data_src.cpp214
-rw-r--r--comm/third_party/botan/src/lib/utils/data_src.h181
-rw-r--r--comm/third_party/botan/src/lib/utils/database.h88
-rw-r--r--comm/third_party/botan/src/lib/utils/donna128.h143
-rw-r--r--comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.cpp82
-rw-r--r--comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.h68
-rw-r--r--comm/third_party/botan/src/lib/utils/dyn_load/info.txt18
-rw-r--r--comm/third_party/botan/src/lib/utils/exceptn.cpp183
-rw-r--r--comm/third_party/botan/src/lib/utils/exceptn.h441
-rw-r--r--comm/third_party/botan/src/lib/utils/filesystem.cpp144
-rw-r--r--comm/third_party/botan/src/lib/utils/filesystem.h33
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/ghash.cpp236
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/ghash.h110
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp207
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/info.txt34
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp62
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/utils/http_util/http_util.cpp267
-rw-r--r--comm/third_party/botan/src/lib/utils/http_util/http_util.h107
-rw-r--r--comm/third_party/botan/src/lib/utils/http_util/info.txt11
-rw-r--r--comm/third_party/botan/src/lib/utils/info.txt43
-rw-r--r--comm/third_party/botan/src/lib/utils/loadstor.h701
-rw-r--r--comm/third_party/botan/src/lib/utils/locking_allocator/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.cpp75
-rw-r--r--comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.h45
-rw-r--r--comm/third_party/botan/src/lib/utils/mem_ops.cpp68
-rw-r--r--comm/third_party/botan/src/lib/utils/mem_ops.h365
-rw-r--r--comm/third_party/botan/src/lib/utils/mem_pool/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.cpp435
-rw-r--r--comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.h58
-rw-r--r--comm/third_party/botan/src/lib/utils/mul128.h125
-rw-r--r--comm/third_party/botan/src/lib/utils/mutex.h60
-rw-r--r--comm/third_party/botan/src/lib/utils/os_utils.cpp757
-rw-r--r--comm/third_party/botan/src/lib/utils/os_utils.h200
-rw-r--r--comm/third_party/botan/src/lib/utils/parsing.cpp458
-rw-r--r--comm/third_party/botan/src/lib/utils/parsing.h181
-rw-r--r--comm/third_party/botan/src/lib/utils/poly_dbl/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.cpp115
-rw-r--r--comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.h39
-rw-r--r--comm/third_party/botan/src/lib/utils/prefetch.h39
-rw-r--r--comm/third_party/botan/src/lib/utils/read_cfg.cpp63
-rw-r--r--comm/third_party/botan/src/lib/utils/read_kv.cpp85
-rw-r--r--comm/third_party/botan/src/lib/utils/rotate.h112
-rw-r--r--comm/third_party/botan/src/lib/utils/rounding.h56
-rw-r--r--comm/third_party/botan/src/lib/utils/safeint.h41
-rw-r--r--comm/third_party/botan/src/lib/utils/simd/info.txt27
-rw-r--r--comm/third_party/botan/src/lib/utils/simd/simd_32.h621
-rw-r--r--comm/third_party/botan/src/lib/utils/simd/simd_avx2/info.txt21
-rw-r--r--comm/third_party/botan/src/lib/utils/simd/simd_avx2/simd_avx2.h299
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/info.txt18
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/socket.cpp371
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/socket.h64
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/socket_udp.cpp344
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/socket_udp.h73
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/uri.cpp188
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/uri.h49
-rw-r--r--comm/third_party/botan/src/lib/utils/sqlite3/info.txt13
-rw-r--r--comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.cpp166
-rw-r--r--comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.h58
-rw-r--r--comm/third_party/botan/src/lib/utils/stl_compatibility.h80
-rw-r--r--comm/third_party/botan/src/lib/utils/stl_util.h110
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/barrier.cpp36
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/barrier.h42
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/info.txt14
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/rwlock.cpp58
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/rwlock.h42
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/semaphore.cpp38
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/semaphore.h34
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.cpp103
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.h82
-rw-r--r--comm/third_party/botan/src/lib/utils/timer.cpp150
-rw-r--r--comm/third_party/botan/src/lib/utils/timer.h185
-rw-r--r--comm/third_party/botan/src/lib/utils/types.h112
-rw-r--r--comm/third_party/botan/src/lib/utils/uuid/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/utils/uuid/uuid.cpp82
-rw-r--r--comm/third_party/botan/src/lib/utils/uuid/uuid.h69
-rw-r--r--comm/third_party/botan/src/lib/utils/version.cpp100
-rw-r--r--comm/third_party/botan/src/lib/utils/version.h101
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, &current_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