summaryrefslogtreecommitdiffstats
path: root/comm/third_party/botan/src/lib/modes
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/botan/src/lib/modes')
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/aead.cpp176
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/aead.h147
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp279
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/ccm/ccm.h130
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/ccm/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp171
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h104
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/eax/eax.cpp194
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/eax/eax.h119
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/eax/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp179
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/gcm/gcm.h117
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/gcm/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/ocb/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp533
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/ocb/ocb.h137
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/siv/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/siv/siv.cpp211
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/siv/siv.h129
-rw-r--r--comm/third_party/botan/src/lib/modes/cbc/cbc.cpp323
-rw-r--r--comm/third_party/botan/src/lib/modes/cbc/cbc.h157
-rw-r--r--comm/third_party/botan/src/lib/modes/cbc/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/modes/cfb/cfb.cpp229
-rw-r--r--comm/third_party/botan/src/lib/modes/cfb/cfb.h106
-rw-r--r--comm/third_party/botan/src/lib/modes/cfb/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/modes/cipher_mode.cpp205
-rw-r--r--comm/third_party/botan/src/lib/modes/cipher_mode.h198
-rw-r--r--comm/third_party/botan/src/lib/modes/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/modes/mode_pad/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp333
-rw-r--r--comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.h160
-rw-r--r--comm/third_party/botan/src/lib/modes/stream_mode.h84
-rw-r--r--comm/third_party/botan/src/lib/modes/xts/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/modes/xts/xts.cpp248
-rw-r--r--comm/third_party/botan/src/lib/modes/xts/xts.h103
37 files changed, 4859 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/modes/aead/aead.cpp b/comm/third_party/botan/src/lib/modes/aead/aead.cpp
new file mode 100644
index 0000000000..9ad550ffd7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/aead.cpp
@@ -0,0 +1,176 @@
+/*
+* (C) 2013,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/aead.h>
+#include <botan/scan_name.h>
+#include <botan/parsing.h>
+#include <sstream>
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+ #include <botan/block_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_CCM)
+ #include <botan/ccm.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
+ #include <botan/chacha20poly1305.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_EAX)
+ #include <botan/eax.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_GCM)
+ #include <botan/gcm.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_OCB)
+ #include <botan/ocb.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_SIV)
+ #include <botan/siv.h>
+#endif
+
+namespace Botan {
+
+void AEAD_Mode::set_associated_data_n(size_t i, const uint8_t ad[], size_t ad_len)
+ {
+ if(i == 0)
+ this->set_associated_data(ad, ad_len);
+ else
+ throw Invalid_Argument("AEAD '" + name() + "' does not support multiple associated data");
+ }
+
+std::unique_ptr<AEAD_Mode> AEAD_Mode::create_or_throw(const std::string& algo,
+ Cipher_Dir dir,
+ const std::string& provider)
+ {
+ if(auto aead = AEAD_Mode::create(algo, dir, provider))
+ return aead;
+
+ throw Lookup_Error("AEAD", algo, provider);
+ }
+
+std::unique_ptr<AEAD_Mode> AEAD_Mode::create(const std::string& algo,
+ Cipher_Dir dir,
+ const std::string& provider)
+ {
+ BOTAN_UNUSED(provider);
+#if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
+ if(algo == "ChaCha20Poly1305")
+ {
+ if(dir == ENCRYPTION)
+ return std::unique_ptr<AEAD_Mode>(new ChaCha20Poly1305_Encryption);
+ else
+ return std::unique_ptr<AEAD_Mode>(new ChaCha20Poly1305_Decryption);
+
+ }
+#endif
+
+ if(algo.find('/') != std::string::npos)
+ {
+ const std::vector<std::string> algo_parts = split_on(algo, '/');
+ const std::string cipher_name = algo_parts[0];
+ const std::vector<std::string> mode_info = parse_algorithm_name(algo_parts[1]);
+
+ if(mode_info.empty())
+ return std::unique_ptr<AEAD_Mode>();
+
+ std::ostringstream alg_args;
+
+ alg_args << '(' << cipher_name;
+ for(size_t i = 1; i < mode_info.size(); ++i)
+ alg_args << ',' << mode_info[i];
+ for(size_t i = 2; i < algo_parts.size(); ++i)
+ alg_args << ',' << algo_parts[i];
+ alg_args << ')';
+
+ const std::string mode_name = mode_info[0] + alg_args.str();
+ return AEAD_Mode::create(mode_name, dir);
+ }
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+
+ SCAN_Name req(algo);
+
+ if(req.arg_count() == 0)
+ {
+ return std::unique_ptr<AEAD_Mode>();
+ }
+
+ std::unique_ptr<BlockCipher> bc(BlockCipher::create(req.arg(0), provider));
+
+ if(!bc)
+ {
+ return std::unique_ptr<AEAD_Mode>();
+ }
+
+#if defined(BOTAN_HAS_AEAD_CCM)
+ if(req.algo_name() == "CCM")
+ {
+ size_t tag_len = req.arg_as_integer(1, 16);
+ size_t L_len = req.arg_as_integer(2, 3);
+ if(dir == ENCRYPTION)
+ return std::unique_ptr<AEAD_Mode>(new CCM_Encryption(bc.release(), tag_len, L_len));
+ else
+ return std::unique_ptr<AEAD_Mode>(new CCM_Decryption(bc.release(), tag_len, L_len));
+ }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_GCM)
+ if(req.algo_name() == "GCM")
+ {
+ size_t tag_len = req.arg_as_integer(1, 16);
+ if(dir == ENCRYPTION)
+ return std::unique_ptr<AEAD_Mode>(new GCM_Encryption(bc.release(), tag_len));
+ else
+ return std::unique_ptr<AEAD_Mode>(new GCM_Decryption(bc.release(), tag_len));
+ }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_OCB)
+ if(req.algo_name() == "OCB")
+ {
+ size_t tag_len = req.arg_as_integer(1, 16);
+ if(dir == ENCRYPTION)
+ return std::unique_ptr<AEAD_Mode>(new OCB_Encryption(bc.release(), tag_len));
+ else
+ return std::unique_ptr<AEAD_Mode>(new OCB_Decryption(bc.release(), tag_len));
+ }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_EAX)
+ if(req.algo_name() == "EAX")
+ {
+ size_t tag_len = req.arg_as_integer(1, bc->block_size());
+ if(dir == ENCRYPTION)
+ return std::unique_ptr<AEAD_Mode>(new EAX_Encryption(bc.release(), tag_len));
+ else
+ return std::unique_ptr<AEAD_Mode>(new EAX_Decryption(bc.release(), tag_len));
+ }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_SIV)
+ if(req.algo_name() == "SIV")
+ {
+ if(dir == ENCRYPTION)
+ return std::unique_ptr<AEAD_Mode>(new SIV_Encryption(bc.release()));
+ else
+ return std::unique_ptr<AEAD_Mode>(new SIV_Decryption(bc.release()));
+ }
+#endif
+
+#endif
+
+ return std::unique_ptr<AEAD_Mode>();
+ }
+
+
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/aead.h b/comm/third_party/botan/src/lib/modes/aead/aead.h
new file mode 100644
index 0000000000..442eb8ed7f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/aead.h
@@ -0,0 +1,147 @@
+/*
+* Interface for AEAD modes
+* (C) 2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_MODE_H_
+#define BOTAN_AEAD_MODE_H_
+
+#include <botan/cipher_mode.h>
+
+namespace Botan {
+
+/**
+* Interface for AEAD (Authenticated Encryption with Associated Data)
+* modes. These modes provide both encryption and message
+* authentication, and can authenticate additional per-message data
+* which is not included in the ciphertext (for instance a sequence
+* number).
+*/
+class BOTAN_PUBLIC_API(2,0) AEAD_Mode : public Cipher_Mode
+ {
+ public:
+ /**
+ * Create an AEAD mode
+ * @param algo the algorithm to create
+ * @param direction specify if this should be an encryption or decryption AEAD
+ * @param provider optional specification for provider to use
+ * @return an AEAD mode or a null pointer if not available
+ */
+ static std::unique_ptr<AEAD_Mode> create(const std::string& algo,
+ Cipher_Dir direction,
+ const std::string& provider = "");
+
+ /**
+ * Create an AEAD mode, or throw
+ * @param algo the algorithm to create
+ * @param direction specify if this should be an encryption or decryption AEAD
+ * @param provider optional specification for provider to use
+ * @return an AEAD mode, or throw an exception
+ */
+ static std::unique_ptr<AEAD_Mode> create_or_throw(const std::string& algo,
+ Cipher_Dir direction,
+ const std::string& provider = "");
+
+ bool authenticated() const override { return true; }
+
+ /**
+ * Set associated data that is not included in the ciphertext but
+ * that should be authenticated. Must be called after set_key and
+ * before start.
+ *
+ * Unless reset by another call, the associated data is kept
+ * between messages. Thus, if the AD does not change, calling
+ * once (after set_key) is the optimum.
+ *
+ * @param ad the associated data
+ * @param ad_len length of add in bytes
+ */
+ virtual void set_associated_data(const uint8_t ad[], size_t ad_len) = 0;
+
+ /**
+ * Set associated data that is not included in the ciphertext but
+ * that should be authenticated. Must be called after set_key and
+ * before start.
+ *
+ * Unless reset by another call, the associated data is kept
+ * between messages. Thus, if the AD does not change, calling
+ * once (after set_key) is the optimum.
+ *
+ * Some AEADs (namely SIV) support multiple AD inputs. For
+ * all other modes only nominal AD input 0 is supported; all
+ * other values of i will cause an exception.
+ *
+ * @param ad the associated data
+ * @param ad_len length of add in bytes
+ */
+ virtual void set_associated_data_n(size_t i, const uint8_t ad[], size_t ad_len);
+
+ /**
+ * Returns the maximum supported number of associated data inputs which
+ * can be provided to set_associated_data_n
+ *
+ * If returns 0, then no associated data is supported.
+ */
+ virtual size_t maximum_associated_data_inputs() const { return 1; }
+
+ /**
+ * Most AEADs require the key to be set prior to setting the AD
+ * A few allow the AD to be set even before the cipher is keyed.
+ * Such ciphers would return false from this function.
+ */
+ virtual bool associated_data_requires_key() const { return true; }
+
+ /**
+ * Set associated data that is not included in the ciphertext but
+ * that should be authenticated. Must be called after set_key and
+ * before start.
+ *
+ * See @ref set_associated_data().
+ *
+ * @param ad the associated data
+ */
+ template<typename Alloc>
+ void set_associated_data_vec(const std::vector<uint8_t, Alloc>& ad)
+ {
+ set_associated_data(ad.data(), ad.size());
+ }
+
+ /**
+ * Set associated data that is not included in the ciphertext but
+ * that should be authenticated. Must be called after set_key and
+ * before start.
+ *
+ * See @ref set_associated_data().
+ *
+ * @param ad the associated data
+ */
+ template<typename Alloc>
+ void set_ad(const std::vector<uint8_t, Alloc>& ad)
+ {
+ set_associated_data(ad.data(), ad.size());
+ }
+
+ /**
+ * @return default AEAD nonce size (a commonly supported value among AEAD
+ * modes, and large enough that random collisions are unlikely)
+ */
+ size_t default_nonce_length() const override { return 12; }
+
+ virtual ~AEAD_Mode() = default;
+ };
+
+/**
+* Get an AEAD mode by name (eg "AES-128/GCM" or "Serpent/EAX")
+* @param name AEAD name
+* @param direction ENCRYPTION or DECRYPTION
+*/
+inline AEAD_Mode* get_aead(const std::string& name, Cipher_Dir direction)
+ {
+ return AEAD_Mode::create(name, direction, "").release();
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp b/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp
new file mode 100644
index 0000000000..ab2cfcb950
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp
@@ -0,0 +1,279 @@
+/*
+* CCM Mode Encryption
+* (C) 2013,2018 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ccm.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+// 128-bit cipher is intrinsic to CCM definition
+static const size_t CCM_BS = 16;
+
+/*
+* CCM_Mode Constructor
+*/
+CCM_Mode::CCM_Mode(BlockCipher* cipher, size_t tag_size, size_t L) :
+ m_tag_size(tag_size),
+ m_L(L),
+ m_cipher(cipher)
+ {
+ if(m_cipher->block_size() != CCM_BS)
+ throw Invalid_Argument(m_cipher->name() + " cannot be used with CCM mode");
+
+ if(L < 2 || L > 8)
+ throw Invalid_Argument("Invalid CCM L value " + std::to_string(L));
+
+ if(tag_size < 4 || tag_size > 16 || tag_size % 2 != 0)
+ throw Invalid_Argument("invalid CCM tag length " + std::to_string(tag_size));
+ }
+
+void CCM_Mode::clear()
+ {
+ m_cipher->clear();
+ reset();
+ }
+
+void CCM_Mode::reset()
+ {
+ m_nonce.clear();
+ m_msg_buf.clear();
+ m_ad_buf.clear();
+ }
+
+std::string CCM_Mode::name() const
+ {
+ return (m_cipher->name() + "/CCM(" + std::to_string(tag_size()) + "," + std::to_string(L())) + ")";
+ }
+
+bool CCM_Mode::valid_nonce_length(size_t n) const
+ {
+ return (n == (15-L()));
+ }
+
+size_t CCM_Mode::default_nonce_length() const
+ {
+ return (15-L());
+ }
+
+size_t CCM_Mode::update_granularity() const
+ {
+ /*
+ This value does not particularly matter as regardless CCM_Mode::update
+ buffers all input, so in theory this could be 1. However as for instance
+ Transform_Filter creates update_granularity() uint8_t buffers, use a
+ somewhat large size to avoid bouncing on a tiny buffer.
+ */
+ return m_cipher->parallel_bytes();
+ }
+
+Key_Length_Specification CCM_Mode::key_spec() const
+ {
+ return m_cipher->key_spec();
+ }
+
+void CCM_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_cipher->set_key(key, length);
+ }
+
+void CCM_Mode::set_associated_data(const uint8_t ad[], size_t length)
+ {
+ m_ad_buf.clear();
+
+ if(length)
+ {
+ // FIXME: support larger AD using length encoding rules
+ BOTAN_ARG_CHECK(length < (0xFFFF - 0xFF), "Supported CCM AD length");
+
+ m_ad_buf.push_back(get_byte(0, static_cast<uint16_t>(length)));
+ m_ad_buf.push_back(get_byte(1, static_cast<uint16_t>(length)));
+ m_ad_buf += std::make_pair(ad, length);
+ while(m_ad_buf.size() % CCM_BS)
+ m_ad_buf.push_back(0); // pad with zeros to full block size
+ }
+ }
+
+void CCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ m_nonce.assign(nonce, nonce + nonce_len);
+ m_msg_buf.clear();
+ }
+
+size_t CCM_Mode::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(m_nonce.size() > 0);
+ m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
+ return 0; // no output until finished
+ }
+
+void CCM_Mode::encode_length(uint64_t len, uint8_t out[])
+ {
+ const size_t len_bytes = L();
+
+ BOTAN_ASSERT_NOMSG(len_bytes >= 2 && len_bytes <= 8);
+
+ for(size_t i = 0; i != len_bytes; ++i)
+ out[len_bytes-1-i] = get_byte(sizeof(uint64_t)-1-i, len);
+
+ if(len_bytes < 8 && (len >> (len_bytes*8)) > 0)
+ throw Encoding_Error("CCM message length too long to encode in L field");
+ }
+
+void CCM_Mode::inc(secure_vector<uint8_t>& C)
+ {
+ for(size_t i = 0; i != C.size(); ++i)
+ if(++C[C.size()-i-1])
+ break;
+ }
+
+secure_vector<uint8_t> CCM_Mode::format_b0(size_t sz)
+ {
+ if(m_nonce.size() != 15-L())
+ throw Invalid_State("CCM mode must set nonce");
+ secure_vector<uint8_t> B0(CCM_BS);
+
+ const uint8_t b_flags =
+ static_cast<uint8_t>((m_ad_buf.size() ? 64 : 0) + (((tag_size()/2)-1) << 3) + (L()-1));
+
+ B0[0] = b_flags;
+ copy_mem(&B0[1], m_nonce.data(), m_nonce.size());
+ encode_length(sz, &B0[m_nonce.size()+1]);
+
+ return B0;
+ }
+
+secure_vector<uint8_t> CCM_Mode::format_c0()
+ {
+ if(m_nonce.size() != 15-L())
+ throw Invalid_State("CCM mode must set nonce");
+ secure_vector<uint8_t> C(CCM_BS);
+
+ const uint8_t a_flags = static_cast<uint8_t>(L() - 1);
+
+ C[0] = a_flags;
+ copy_mem(&C[1], m_nonce.data(), m_nonce.size());
+
+ return C;
+ }
+
+void CCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is sane");
+
+ buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
+
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ const secure_vector<uint8_t>& ad = ad_buf();
+ BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
+
+ const BlockCipher& E = cipher();
+
+ secure_vector<uint8_t> T(CCM_BS);
+ E.encrypt(format_b0(sz), T);
+
+ for(size_t i = 0; i != ad.size(); i += CCM_BS)
+ {
+ xor_buf(T.data(), &ad[i], CCM_BS);
+ E.encrypt(T);
+ }
+
+ secure_vector<uint8_t> C = format_c0();
+ secure_vector<uint8_t> S0(CCM_BS);
+ E.encrypt(C, S0);
+ inc(C);
+
+ secure_vector<uint8_t> X(CCM_BS);
+
+ const uint8_t* buf_end = &buf[sz];
+
+ while(buf != buf_end)
+ {
+ const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
+
+ xor_buf(T.data(), buf, to_proc);
+ E.encrypt(T);
+
+ E.encrypt(C, X);
+ xor_buf(buf, X.data(), to_proc);
+ inc(C);
+
+ buf += to_proc;
+ }
+
+ T ^= S0;
+
+ buffer += std::make_pair(T.data(), tag_size());
+
+ reset();
+ }
+
+void CCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is sane");
+
+ buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
+
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ BOTAN_ASSERT(sz >= tag_size(), "We have the tag");
+
+ const secure_vector<uint8_t>& ad = ad_buf();
+ BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
+
+ const BlockCipher& E = cipher();
+
+ secure_vector<uint8_t> T(CCM_BS);
+ E.encrypt(format_b0(sz - tag_size()), T);
+
+ for(size_t i = 0; i != ad.size(); i += CCM_BS)
+ {
+ xor_buf(T.data(), &ad[i], CCM_BS);
+ E.encrypt(T);
+ }
+
+ secure_vector<uint8_t> C = format_c0();
+
+ secure_vector<uint8_t> S0(CCM_BS);
+ E.encrypt(C, S0);
+ inc(C);
+
+ secure_vector<uint8_t> X(CCM_BS);
+
+ const uint8_t* buf_end = &buf[sz - tag_size()];
+
+ while(buf != buf_end)
+ {
+ const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
+
+ E.encrypt(C, X);
+ xor_buf(buf, X.data(), to_proc);
+ inc(C);
+
+ xor_buf(T.data(), buf, to_proc);
+ E.encrypt(T);
+
+ buf += to_proc;
+ }
+
+ T ^= S0;
+
+ if(!constant_time_compare(T.data(), buf_end, tag_size()))
+ throw Invalid_Authentication_Tag("CCM tag check failed");
+
+ buffer.resize(buffer.size() - tag_size());
+
+ reset();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.h b/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.h
new file mode 100644
index 0000000000..9b4bcecbf2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.h
@@ -0,0 +1,130 @@
+/*
+* CCM Mode
+* (C) 2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_CCM_H_
+#define BOTAN_AEAD_CCM_H_
+
+#include <botan/aead.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(ccm.h)
+
+namespace Botan {
+
+/**
+* Base class for CCM encryption and decryption
+* @see RFC 3610
+*/
+class BOTAN_PUBLIC_API(2,0) CCM_Mode : public AEAD_Mode
+ {
+ public:
+ size_t process(uint8_t buf[], size_t sz) override;
+
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+ bool associated_data_requires_key() const override { return false; }
+
+ std::string name() const override;
+
+ size_t update_granularity() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ bool valid_nonce_length(size_t) const override;
+
+ size_t default_nonce_length() const override;
+
+ void clear() override;
+
+ void reset() override;
+
+ size_t tag_size() const override { return m_tag_size; }
+
+ protected:
+ CCM_Mode(BlockCipher* cipher, size_t tag_size, size_t L);
+
+ size_t L() const { return m_L; }
+
+ const BlockCipher& cipher() const { return *m_cipher; }
+
+ void encode_length(uint64_t len, uint8_t out[]);
+
+ void inc(secure_vector<uint8_t>& C);
+
+ const secure_vector<uint8_t>& ad_buf() const { return m_ad_buf; }
+
+ secure_vector<uint8_t>& msg_buf() { return m_msg_buf; }
+
+ secure_vector<uint8_t> format_b0(size_t msg_size);
+ secure_vector<uint8_t> format_c0();
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ const size_t m_tag_size;
+ const size_t m_L;
+
+ std::unique_ptr<BlockCipher> m_cipher;
+ secure_vector<uint8_t> m_nonce, m_msg_buf, m_ad_buf;
+ };
+
+/**
+* CCM Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) CCM_Encryption final : public CCM_Mode
+ {
+ public:
+ /**
+ * @param cipher a 128-bit block cipher
+ * @param tag_size is how big the auth tag will be (even values
+ * between 4 and 16 are accepted)
+ * @param L length of L parameter. The total message length
+ * must be less than 2**L bytes, and the nonce is 15-L bytes.
+ */
+ CCM_Encryption(BlockCipher* cipher, size_t tag_size = 16, size_t L = 3) :
+ CCM_Mode(cipher, tag_size, L) {}
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override
+ { return input_length + tag_size(); }
+
+ size_t minimum_final_size() const override { return 0; }
+ };
+
+/**
+* CCM Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) CCM_Decryption final : public CCM_Mode
+ {
+ public:
+ /**
+ * @param cipher a 128-bit block cipher
+ * @param tag_size is how big the auth tag will be (even values
+ * between 4 and 16 are accepted)
+ * @param L length of L parameter. The total message length
+ * must be less than 2**L bytes, and the nonce is 15-L bytes.
+ */
+ CCM_Decryption(BlockCipher* cipher, size_t tag_size = 16, size_t L = 3) :
+ CCM_Mode(cipher, tag_size, L) {}
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override
+ {
+ BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+ return input_length - tag_size();
+ }
+
+ size_t minimum_final_size() const override { return tag_size(); }
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/aead/ccm/info.txt b/comm/third_party/botan/src/lib/modes/aead/ccm/info.txt
new file mode 100644
index 0000000000..9341cea7fc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/ccm/info.txt
@@ -0,0 +1,7 @@
+<defines>
+AEAD_CCM -> 20131128
+</defines>
+
+<requires>
+block
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
new file mode 100644
index 0000000000..ca02ca5048
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
@@ -0,0 +1,171 @@
+/*
+* ChaCha20Poly1305 AEAD
+* (C) 2014,2016,2018 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/chacha20poly1305.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+ChaCha20Poly1305_Mode::ChaCha20Poly1305_Mode() :
+ m_chacha(StreamCipher::create("ChaCha")),
+ m_poly1305(MessageAuthenticationCode::create("Poly1305"))
+ {
+ if(!m_chacha || !m_poly1305)
+ throw Algorithm_Not_Found("ChaCha20Poly1305");
+ }
+
+bool ChaCha20Poly1305_Mode::valid_nonce_length(size_t n) const
+ {
+ return (n == 8 || n == 12 || n == 24);
+ }
+
+void ChaCha20Poly1305_Mode::clear()
+ {
+ m_chacha->clear();
+ m_poly1305->clear();
+ reset();
+ }
+
+void ChaCha20Poly1305_Mode::reset()
+ {
+ m_ad.clear();
+ m_ctext_len = 0;
+ m_nonce_len = 0;
+ }
+
+void ChaCha20Poly1305_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_chacha->set_key(key, length);
+ }
+
+void ChaCha20Poly1305_Mode::set_associated_data(const uint8_t ad[], size_t length)
+ {
+ if(m_ctext_len > 0 || m_nonce_len > 0)
+ throw Invalid_State("Cannot set AD for ChaCha20Poly1305 while processing a message");
+ m_ad.assign(ad, ad + length);
+ }
+
+void ChaCha20Poly1305_Mode::update_len(size_t len)
+ {
+ uint8_t len8[8] = { 0 };
+ store_le(static_cast<uint64_t>(len), len8);
+ m_poly1305->update(len8, 8);
+ }
+
+void ChaCha20Poly1305_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ m_ctext_len = 0;
+ m_nonce_len = nonce_len;
+
+ m_chacha->set_iv(nonce, nonce_len);
+
+ uint8_t first_block[64];
+ m_chacha->write_keystream(first_block, sizeof(first_block));
+
+ m_poly1305->set_key(first_block, 32);
+ // Remainder of first block is discarded
+ secure_scrub_memory(first_block, sizeof(first_block));
+
+ m_poly1305->update(m_ad);
+
+ if(cfrg_version())
+ {
+ if(m_ad.size() % 16)
+ {
+ const uint8_t zeros[16] = { 0 };
+ m_poly1305->update(zeros, 16 - m_ad.size() % 16);
+ }
+ }
+ else
+ {
+ update_len(m_ad.size());
+ }
+ }
+
+size_t ChaCha20Poly1305_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ m_chacha->cipher1(buf, sz);
+ m_poly1305->update(buf, sz); // poly1305 of ciphertext
+ m_ctext_len += sz;
+ return sz;
+ }
+
+void ChaCha20Poly1305_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ update(buffer, offset);
+ if(cfrg_version())
+ {
+ if(m_ctext_len % 16)
+ {
+ const uint8_t zeros[16] = { 0 };
+ m_poly1305->update(zeros, 16 - m_ctext_len % 16);
+ }
+ update_len(m_ad.size());
+ }
+ update_len(m_ctext_len);
+
+ buffer.resize(buffer.size() + tag_size());
+ m_poly1305->final(&buffer[buffer.size() - tag_size()]);
+ m_ctext_len = 0;
+ m_nonce_len = 0;
+ }
+
+size_t ChaCha20Poly1305_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ m_poly1305->update(buf, sz); // poly1305 of ciphertext
+ m_chacha->cipher1(buf, sz);
+ m_ctext_len += sz;
+ return sz;
+ }
+
+void ChaCha20Poly1305_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ BOTAN_ASSERT(sz >= tag_size(), "Have the tag as part of final input");
+
+ const size_t remaining = sz - tag_size();
+
+ if(remaining)
+ {
+ m_poly1305->update(buf, remaining); // poly1305 of ciphertext
+ m_chacha->cipher1(buf, remaining);
+ m_ctext_len += remaining;
+ }
+
+ if(cfrg_version())
+ {
+ if(m_ctext_len % 16)
+ {
+ const uint8_t zeros[16] = { 0 };
+ m_poly1305->update(zeros, 16 - m_ctext_len % 16);
+ }
+ update_len(m_ad.size());
+ }
+
+ update_len(m_ctext_len);
+
+ uint8_t mac[16];
+ m_poly1305->final(mac);
+
+ const uint8_t* included_tag = &buf[remaining];
+
+ m_ctext_len = 0;
+ m_nonce_len = 0;
+
+ if(!constant_time_compare(mac, included_tag, tag_size()))
+ throw Invalid_Authentication_Tag("ChaCha20Poly1305 tag check failed");
+ buffer.resize(offset + remaining);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h
new file mode 100644
index 0000000000..dbba58cc93
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h
@@ -0,0 +1,104 @@
+/*
+* ChaCha20Poly1305 AEAD
+* (C) 2014 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_CHACHA20_POLY1305_H_
+#define BOTAN_AEAD_CHACHA20_POLY1305_H_
+
+#include <botan/aead.h>
+#include <botan/stream_cipher.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(chacha20poly1305.h)
+
+namespace Botan {
+
+/**
+* Base class
+* See draft-irtf-cfrg-chacha20-poly1305-03 for specification
+* If a nonce of 64 bits is used the older version described in
+* draft-agl-tls-chacha20poly1305-04 is used instead.
+* If a nonce of 192 bits is used, XChaCha20Poly1305 is selected.
+*/
+class BOTAN_PUBLIC_API(2,0) ChaCha20Poly1305_Mode : public AEAD_Mode
+ {
+ public:
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+ bool associated_data_requires_key() const override { return false; }
+
+ std::string name() const override { return "ChaCha20Poly1305"; }
+
+ size_t update_granularity() const override { return 64; }
+
+ Key_Length_Specification key_spec() const override
+ { return Key_Length_Specification(32); }
+
+ bool valid_nonce_length(size_t n) const override;
+
+ size_t tag_size() const override { return 16; }
+
+ void clear() override;
+
+ void reset() override;
+
+ protected:
+ std::unique_ptr<StreamCipher> m_chacha;
+ std::unique_ptr<MessageAuthenticationCode> m_poly1305;
+
+ ChaCha20Poly1305_Mode();
+
+ secure_vector<uint8_t> m_ad;
+ size_t m_nonce_len = 0;
+ size_t m_ctext_len = 0;
+
+ bool cfrg_version() const { return m_nonce_len == 12 || m_nonce_len == 24; }
+ void update_len(size_t len);
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+ };
+
+/**
+* ChaCha20Poly1305 Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) ChaCha20Poly1305_Encryption final : public ChaCha20Poly1305_Mode
+ {
+ public:
+ size_t output_length(size_t input_length) const override
+ { return input_length + tag_size(); }
+
+ size_t minimum_final_size() const override { return 0; }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+/**
+* ChaCha20Poly1305 Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) ChaCha20Poly1305_Decryption final : public ChaCha20Poly1305_Mode
+ {
+ public:
+ size_t output_length(size_t input_length) const override
+ {
+ BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+ return input_length - tag_size();
+ }
+
+ size_t minimum_final_size() const override { return tag_size(); }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/info.txt b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/info.txt
new file mode 100644
index 0000000000..c3ea53db09
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/info.txt
@@ -0,0 +1,8 @@
+<defines>
+AEAD_CHACHA20_POLY1305 -> 20180807
+</defines>
+
+<requires>
+chacha
+poly1305
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/aead/eax/eax.cpp b/comm/third_party/botan/src/lib/modes/aead/eax/eax.cpp
new file mode 100644
index 0000000000..4f6b540ffb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/eax/eax.cpp
@@ -0,0 +1,194 @@
+/*
+* EAX Mode Encryption
+* (C) 1999-2007 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/eax.h>
+#include <botan/cmac.h>
+#include <botan/ctr.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* EAX MAC-based PRF
+*/
+secure_vector<uint8_t> eax_prf(uint8_t tag, size_t block_size,
+ MessageAuthenticationCode& mac,
+ const uint8_t in[], size_t length)
+ {
+ for(size_t i = 0; i != block_size - 1; ++i)
+ {
+ mac.update(0);
+ }
+ mac.update(tag);
+ mac.update(in, length);
+ return mac.final();
+ }
+
+}
+
+/*
+* EAX_Mode Constructor
+*/
+EAX_Mode::EAX_Mode(BlockCipher* cipher, size_t tag_size) :
+ m_tag_size(tag_size),
+ m_cipher(cipher),
+ m_ctr(new CTR_BE(m_cipher->clone())),
+ m_cmac(new CMAC(m_cipher->clone()))
+ {
+ if(m_tag_size < 8 || m_tag_size > m_cmac->output_length())
+ throw Invalid_Argument(name() + ": Bad tag size " + std::to_string(tag_size));
+ }
+
+void EAX_Mode::clear()
+ {
+ m_cipher->clear();
+ m_ctr->clear();
+ m_cmac->clear();
+ reset();
+ }
+
+void EAX_Mode::reset()
+ {
+ m_ad_mac.clear();
+ m_nonce_mac.clear();
+
+ // Clear out any data added to the CMAC calculation
+ try {
+ m_cmac->final();
+ }
+ catch(Key_Not_Set&) {}
+ }
+
+std::string EAX_Mode::name() const
+ {
+ return (m_cipher->name() + "/EAX");
+ }
+
+size_t EAX_Mode::update_granularity() const
+ {
+ /*
+ * For EAX this actually can be as low as 1 but that causes problems
+ * for applications which use update_granularity as the buffer size.
+ */
+ return m_cipher->parallel_bytes();
+ }
+
+Key_Length_Specification EAX_Mode::key_spec() const
+ {
+ return m_cipher->key_spec();
+ }
+
+/*
+* Set the EAX key
+*/
+void EAX_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ /*
+ * These could share the key schedule, which is one nice part of EAX,
+ * but it's much easier to ignore that here...
+ */
+ m_ctr->set_key(key, length);
+ m_cmac->set_key(key, length);
+ }
+
+/*
+* Set the EAX associated data
+*/
+void EAX_Mode::set_associated_data(const uint8_t ad[], size_t length)
+ {
+ if(m_nonce_mac.empty() == false)
+ throw Invalid_State("Cannot set AD for EAX while processing a message");
+ m_ad_mac = eax_prf(1, block_size(), *m_cmac, ad, length);
+ }
+
+void EAX_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ m_nonce_mac = eax_prf(0, block_size(), *m_cmac, nonce, nonce_len);
+
+ m_ctr->set_iv(m_nonce_mac.data(), m_nonce_mac.size());
+
+ for(size_t i = 0; i != block_size() - 1; ++i)
+ m_cmac->update(0);
+ m_cmac->update(2);
+ }
+
+size_t EAX_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(m_nonce_mac.size() > 0);
+ m_ctr->cipher(buf, buf, sz);
+ m_cmac->update(buf, sz);
+ return sz;
+ }
+
+void EAX_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT_NOMSG(m_nonce_mac.empty() == false);
+ update(buffer, offset);
+
+ secure_vector<uint8_t> data_mac = m_cmac->final();
+ xor_buf(data_mac, m_nonce_mac, data_mac.size());
+
+ if(m_ad_mac.empty())
+ {
+ m_ad_mac = eax_prf(1, block_size(), *m_cmac, nullptr, 0);
+ }
+
+ xor_buf(data_mac, m_ad_mac, data_mac.size());
+
+ buffer += std::make_pair(data_mac.data(), tag_size());
+ }
+
+size_t EAX_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(m_nonce_mac.size() > 0);
+ m_cmac->update(buf, sz);
+ m_ctr->cipher(buf, buf, sz);
+ return sz;
+ }
+
+void EAX_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ BOTAN_ASSERT(sz >= tag_size(), "Have the tag as part of final input");
+
+ const size_t remaining = sz - tag_size();
+
+ if(remaining)
+ {
+ m_cmac->update(buf, remaining);
+ m_ctr->cipher(buf, buf, remaining);
+ }
+
+ const uint8_t* included_tag = &buf[remaining];
+
+ secure_vector<uint8_t> mac = m_cmac->final();
+ mac ^= m_nonce_mac;
+
+ if(m_ad_mac.empty())
+ {
+ m_ad_mac = eax_prf(1, block_size(), *m_cmac, nullptr, 0);
+ }
+
+ mac ^= m_ad_mac;
+
+ if(!constant_time_compare(mac.data(), included_tag, tag_size()))
+ throw Invalid_Authentication_Tag("EAX tag check failed");
+
+ buffer.resize(offset + remaining);
+
+ m_nonce_mac.clear();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/eax/eax.h b/comm/third_party/botan/src/lib/modes/aead/eax/eax.h
new file mode 100644
index 0000000000..b9b02c192b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/eax/eax.h
@@ -0,0 +1,119 @@
+/*
+* EAX Mode
+* (C) 1999-2007,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_EAX_H_
+#define BOTAN_AEAD_EAX_H_
+
+#include <botan/aead.h>
+#include <botan/block_cipher.h>
+#include <botan/stream_cipher.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(eax.h)
+
+namespace Botan {
+
+/**
+* EAX base class
+*/
+class BOTAN_PUBLIC_API(2,0) EAX_Mode : public AEAD_Mode
+ {
+ public:
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+ std::string name() const override;
+
+ size_t update_granularity() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ // EAX supports arbitrary nonce lengths
+ bool valid_nonce_length(size_t) const override { return true; }
+
+ size_t tag_size() const override { return m_tag_size; }
+
+ void clear() override;
+
+ void reset() override;
+
+ protected:
+ /**
+ * @param cipher the cipher to use
+ * @param tag_size is how big the auth tag will be
+ */
+ EAX_Mode(BlockCipher* cipher, size_t tag_size);
+
+ size_t block_size() const { return m_cipher->block_size(); }
+
+ size_t m_tag_size;
+
+ std::unique_ptr<BlockCipher> m_cipher;
+ std::unique_ptr<StreamCipher> m_ctr;
+ std::unique_ptr<MessageAuthenticationCode> m_cmac;
+
+ secure_vector<uint8_t> m_ad_mac;
+
+ secure_vector<uint8_t> m_nonce_mac;
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+ };
+
+/**
+* EAX Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) EAX_Encryption final : public EAX_Mode
+ {
+ public:
+ /**
+ * @param cipher a 128-bit block cipher
+ * @param tag_size is how big the auth tag will be
+ */
+ EAX_Encryption(BlockCipher* cipher, size_t tag_size = 0) :
+ EAX_Mode(cipher, tag_size) {}
+
+ size_t output_length(size_t input_length) const override
+ { return input_length + tag_size(); }
+
+ size_t minimum_final_size() const override { return 0; }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+/**
+* EAX Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) EAX_Decryption final : public EAX_Mode
+ {
+ public:
+ /**
+ * @param cipher a 128-bit block cipher
+ * @param tag_size is how big the auth tag will be
+ */
+ EAX_Decryption(BlockCipher* cipher, size_t tag_size = 0) :
+ EAX_Mode(cipher, tag_size) {}
+
+ size_t output_length(size_t input_length) const override
+ {
+ BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+ return input_length - tag_size();
+ }
+
+ size_t minimum_final_size() const override { return tag_size(); }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/aead/eax/info.txt b/comm/third_party/botan/src/lib/modes/aead/eax/info.txt
new file mode 100644
index 0000000000..6bc01ff704
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/eax/info.txt
@@ -0,0 +1,8 @@
+<defines>
+AEAD_EAX -> 20131128
+</defines>
+
+<requires>
+cmac
+ctr
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp
new file mode 100644
index 0000000000..4b9c5b7204
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp
@@ -0,0 +1,179 @@
+/*
+* GCM Mode Encryption
+* (C) 2013,2015 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/gcm.h>
+#include <botan/ghash.h>
+#include <botan/block_cipher.h>
+#include <botan/ctr.h>
+
+namespace Botan {
+
+/*
+* GCM_Mode Constructor
+*/
+GCM_Mode::GCM_Mode(BlockCipher* cipher, size_t tag_size) :
+ m_tag_size(tag_size),
+ m_cipher_name(cipher->name()),
+ m_ctr(new CTR_BE(cipher, 4)),
+ m_ghash(new GHASH)
+ {
+ if(cipher->block_size() != GCM_BS)
+ throw Invalid_Argument("Invalid block cipher for GCM");
+
+ /* We allow any of the values 128, 120, 112, 104, or 96 bits as a tag size */
+ /* 64 bit tag is still supported but deprecated and will be removed in the future */
+ if(m_tag_size != 8 && (m_tag_size < 12 || m_tag_size > 16))
+ throw Invalid_Argument(name() + ": Bad tag size " + std::to_string(m_tag_size));
+ }
+
+GCM_Mode::~GCM_Mode() { /* for unique_ptr */ }
+
+void GCM_Mode::clear()
+ {
+ m_ctr->clear();
+ m_ghash->clear();
+ reset();
+ }
+
+void GCM_Mode::reset()
+ {
+ m_ghash->reset();
+ }
+
+std::string GCM_Mode::name() const
+ {
+ return (m_cipher_name + "/GCM(" + std::to_string(tag_size()) + ")");
+ }
+
+std::string GCM_Mode::provider() const
+ {
+ return m_ghash->provider();
+ }
+
+size_t GCM_Mode::update_granularity() const
+ {
+ return GCM_BS * std::max<size_t>(2, BOTAN_BLOCK_CIPHER_PAR_MULT);
+ }
+
+bool GCM_Mode::valid_nonce_length(size_t len) const
+ {
+ // GCM does not support empty nonces
+ return (len > 0);
+ }
+
+Key_Length_Specification GCM_Mode::key_spec() const
+ {
+ return m_ctr->key_spec();
+ }
+
+void GCM_Mode::key_schedule(const uint8_t key[], size_t keylen)
+ {
+ m_ctr->set_key(key, keylen);
+
+ const std::vector<uint8_t> zeros(GCM_BS);
+ m_ctr->set_iv(zeros.data(), zeros.size());
+
+ secure_vector<uint8_t> H(GCM_BS);
+ m_ctr->encipher(H);
+ m_ghash->set_key(H);
+ }
+
+void GCM_Mode::set_associated_data(const uint8_t ad[], size_t ad_len)
+ {
+ m_ghash->set_associated_data(ad, ad_len);
+ }
+
+void GCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ if(m_y0.size() != GCM_BS)
+ m_y0.resize(GCM_BS);
+
+ clear_mem(m_y0.data(), m_y0.size());
+
+ if(nonce_len == 12)
+ {
+ copy_mem(m_y0.data(), nonce, nonce_len);
+ m_y0[15] = 1;
+ }
+ else
+ {
+ m_ghash->nonce_hash(m_y0, nonce, nonce_len);
+ }
+
+ m_ctr->set_iv(m_y0.data(), m_y0.size());
+
+ clear_mem(m_y0.data(), m_y0.size());
+ m_ctr->encipher(m_y0);
+
+ m_ghash->start(m_y0.data(), m_y0.size());
+ clear_mem(m_y0.data(), m_y0.size());
+ }
+
+size_t GCM_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size");
+ m_ctr->cipher(buf, buf, sz);
+ m_ghash->update(buf, sz);
+ return sz;
+ }
+
+void GCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ m_ctr->cipher(buf, buf, sz);
+ m_ghash->update(buf, sz);
+
+ uint8_t mac[16] = { 0 };
+ m_ghash->final(mac, tag_size());
+ buffer += std::make_pair(mac, tag_size());
+ }
+
+size_t GCM_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size");
+ m_ghash->update(buf, sz);
+ m_ctr->cipher(buf, buf, sz);
+ return sz;
+ }
+
+void GCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ if(sz < tag_size())
+ throw Decoding_Error("Insufficient input for GCM decryption, tag missing");
+
+ const size_t remaining = sz - tag_size();
+
+ // handle any final input before the tag
+ if(remaining)
+ {
+ m_ghash->update(buf, remaining);
+ m_ctr->cipher(buf, buf, remaining);
+ }
+
+ uint8_t mac[16] = { 0 };
+ m_ghash->final(mac, tag_size());
+
+ const uint8_t* included_tag = &buffer[remaining+offset];
+
+ if(!constant_time_compare(mac, included_tag, tag_size()))
+ throw Invalid_Authentication_Tag("GCM tag check failed");
+
+ buffer.resize(offset + remaining);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.h b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.h
new file mode 100644
index 0000000000..fe3c405721
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.h
@@ -0,0 +1,117 @@
+/*
+* GCM Mode
+* (C) 2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_GCM_H_
+#define BOTAN_AEAD_GCM_H_
+
+#include <botan/aead.h>
+#include <botan/sym_algo.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(gcm.h)
+
+namespace Botan {
+
+class BlockCipher;
+class StreamCipher;
+class GHASH;
+
+/**
+* GCM Mode
+*/
+class BOTAN_PUBLIC_API(2,0) GCM_Mode : public AEAD_Mode
+ {
+ public:
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+ std::string name() const override;
+
+ size_t update_granularity() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ bool valid_nonce_length(size_t len) const override;
+
+ size_t tag_size() const override { return m_tag_size; }
+
+ void clear() override;
+
+ void reset() override;
+
+ std::string provider() const override;
+ protected:
+ GCM_Mode(BlockCipher* cipher, size_t tag_size);
+
+ ~GCM_Mode();
+
+ static const size_t GCM_BS = 16;
+
+ const size_t m_tag_size;
+ const std::string m_cipher_name;
+
+ std::unique_ptr<StreamCipher> m_ctr;
+ std::unique_ptr<GHASH> m_ghash;
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ secure_vector<uint8_t> m_y0;
+ };
+
+/**
+* GCM Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) GCM_Encryption final : public GCM_Mode
+ {
+ public:
+ /**
+ * @param cipher the 128 bit block cipher to use
+ * @param tag_size is how big the auth tag will be
+ */
+ GCM_Encryption(BlockCipher* cipher, size_t tag_size = 16) :
+ GCM_Mode(cipher, tag_size) {}
+
+ size_t output_length(size_t input_length) const override
+ { return input_length + tag_size(); }
+
+ size_t minimum_final_size() const override { return 0; }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+/**
+* GCM Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) GCM_Decryption final : public GCM_Mode
+ {
+ public:
+ /**
+ * @param cipher the 128 bit block cipher to use
+ * @param tag_size is how big the auth tag will be
+ */
+ GCM_Decryption(BlockCipher* cipher, size_t tag_size = 16) :
+ GCM_Mode(cipher, tag_size) {}
+
+ size_t output_length(size_t input_length) const override
+ {
+ BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+ return input_length - tag_size();
+ }
+
+ size_t minimum_final_size() const override { return tag_size(); }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/aead/gcm/info.txt b/comm/third_party/botan/src/lib/modes/aead/gcm/info.txt
new file mode 100644
index 0000000000..94eb09ab64
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/gcm/info.txt
@@ -0,0 +1,9 @@
+<defines>
+AEAD_GCM -> 20131128
+</defines>
+
+<requires>
+block
+ctr
+ghash
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/aead/info.txt b/comm/third_party/botan/src/lib/modes/aead/info.txt
new file mode 100644
index 0000000000..15106e46ab
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/info.txt
@@ -0,0 +1,3 @@
+<defines>
+AEAD_MODES -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/modes/aead/ocb/info.txt b/comm/third_party/botan/src/lib/modes/aead/ocb/info.txt
new file mode 100644
index 0000000000..4e16e23624
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/ocb/info.txt
@@ -0,0 +1,8 @@
+<defines>
+AEAD_OCB -> 20131128
+</defines>
+
+<requires>
+block
+poly_dbl
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp b/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp
new file mode 100644
index 0000000000..6c16203296
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp
@@ -0,0 +1,533 @@
+/*
+* OCB Mode
+* (C) 2013,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ocb.h>
+#include <botan/block_cipher.h>
+#include <botan/internal/poly_dbl.h>
+#include <botan/internal/bit_ops.h>
+
+namespace Botan {
+
+// Has to be in Botan namespace so unique_ptr can reference it
+class L_computer final
+ {
+ public:
+ explicit L_computer(const BlockCipher& cipher) :
+ m_BS(cipher.block_size()),
+ m_max_blocks(cipher.parallel_bytes() / m_BS)
+ {
+ m_L_star.resize(m_BS);
+ cipher.encrypt(m_L_star);
+ m_L_dollar = poly_double(star());
+ m_L.push_back(poly_double(dollar()));
+
+ while(m_L.size() < 8)
+ m_L.push_back(poly_double(m_L.back()));
+
+ m_offset_buf.resize(m_BS * m_max_blocks);
+ }
+
+ void init(const secure_vector<uint8_t>& offset)
+ {
+ m_offset = offset;
+ }
+
+ bool initialized() const { return m_offset.empty() == false; }
+
+ const secure_vector<uint8_t>& star() const { return m_L_star; }
+ const secure_vector<uint8_t>& dollar() const { return m_L_dollar; }
+ const secure_vector<uint8_t>& offset() const { return m_offset; }
+
+ const secure_vector<uint8_t>& get(size_t i) const
+ {
+ while(m_L.size() <= i)
+ m_L.push_back(poly_double(m_L.back()));
+
+ return m_L[i];
+ }
+
+ const uint8_t*
+ compute_offsets(size_t block_index, size_t blocks)
+ {
+ BOTAN_ASSERT(blocks <= m_max_blocks, "OCB offsets");
+
+ uint8_t* offsets = m_offset_buf.data();
+
+ if(block_index % 4 == 0)
+ {
+ const secure_vector<uint8_t>& L0 = get(0);
+ const secure_vector<uint8_t>& L1 = get(1);
+
+ while(blocks >= 4)
+ {
+ // ntz(4*i+1) == 0
+ // ntz(4*i+2) == 1
+ // ntz(4*i+3) == 0
+ block_index += 4;
+ const size_t ntz4 = var_ctz32(static_cast<uint32_t>(block_index));
+
+ xor_buf(offsets, m_offset.data(), L0.data(), m_BS);
+ offsets += m_BS;
+
+ xor_buf(offsets, offsets - m_BS, L1.data(), m_BS);
+ offsets += m_BS;
+
+ xor_buf(m_offset.data(), L1.data(), m_BS);
+ copy_mem(offsets, m_offset.data(), m_BS);
+ offsets += m_BS;
+
+ xor_buf(m_offset.data(), get(ntz4).data(), m_BS);
+ copy_mem(offsets, m_offset.data(), m_BS);
+ offsets += m_BS;
+
+ blocks -= 4;
+ }
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ { // could be done in parallel
+ const size_t ntz = var_ctz32(static_cast<uint32_t>(block_index + i + 1));
+ xor_buf(m_offset.data(), get(ntz).data(), m_BS);
+ copy_mem(offsets, m_offset.data(), m_BS);
+ offsets += m_BS;
+ }
+
+ return m_offset_buf.data();
+ }
+
+ private:
+ secure_vector<uint8_t> poly_double(const secure_vector<uint8_t>& in) const
+ {
+ secure_vector<uint8_t> out(in.size());
+ poly_double_n(out.data(), in.data(), out.size());
+ return out;
+ }
+
+ const size_t m_BS, m_max_blocks;
+ secure_vector<uint8_t> m_L_dollar, m_L_star;
+ secure_vector<uint8_t> m_offset;
+ mutable std::vector<secure_vector<uint8_t>> m_L;
+ secure_vector<uint8_t> m_offset_buf;
+ };
+
+namespace {
+
+/*
+* OCB's HASH
+*/
+secure_vector<uint8_t> ocb_hash(const L_computer& L,
+ const BlockCipher& cipher,
+ const uint8_t ad[], size_t ad_len)
+ {
+ const size_t BS = cipher.block_size();
+ secure_vector<uint8_t> sum(BS);
+ secure_vector<uint8_t> offset(BS);
+
+ secure_vector<uint8_t> buf(BS);
+
+ const size_t ad_blocks = (ad_len / BS);
+ const size_t ad_remainder = (ad_len % BS);
+
+ for(size_t i = 0; i != ad_blocks; ++i)
+ {
+ // this loop could run in parallel
+ offset ^= L.get(var_ctz32(static_cast<uint32_t>(i+1)));
+ buf = offset;
+ xor_buf(buf.data(), &ad[BS*i], BS);
+ cipher.encrypt(buf);
+ sum ^= buf;
+ }
+
+ if(ad_remainder)
+ {
+ offset ^= L.star();
+ buf = offset;
+ xor_buf(buf.data(), &ad[BS*ad_blocks], ad_remainder);
+ buf[ad_remainder] ^= 0x80;
+ cipher.encrypt(buf);
+ sum ^= buf;
+ }
+
+ return sum;
+ }
+
+}
+
+OCB_Mode::OCB_Mode(BlockCipher* cipher, size_t tag_size) :
+ m_cipher(cipher),
+ m_checksum(m_cipher->parallel_bytes()),
+ m_ad_hash(m_cipher->block_size()),
+ m_tag_size(tag_size),
+ m_block_size(m_cipher->block_size()),
+ m_par_blocks(m_cipher->parallel_bytes() / m_block_size)
+ {
+ const size_t BS = block_size();
+
+ /*
+ * draft-krovetz-ocb-wide-d1 specifies OCB for several other block
+ * sizes but only 128, 192, 256 and 512 bit are currently supported
+ * by this implementation.
+ */
+ BOTAN_ARG_CHECK(BS == 16 || BS == 24 || BS == 32 || BS == 64,
+ "Invalid block size for OCB");
+
+ BOTAN_ARG_CHECK(m_tag_size % 4 == 0 &&
+ m_tag_size >= 8 && m_tag_size <= BS &&
+ m_tag_size <= 32,
+ "Invalid OCB tag length");
+ }
+
+OCB_Mode::~OCB_Mode() { /* for unique_ptr destructor */ }
+
+void OCB_Mode::clear()
+ {
+ m_cipher->clear();
+ m_L.reset(); // add clear here?
+ reset();
+ }
+
+void OCB_Mode::reset()
+ {
+ m_block_index = 0;
+ zeroise(m_ad_hash);
+ zeroise(m_checksum);
+ m_last_nonce.clear();
+ m_stretch.clear();
+ }
+
+bool OCB_Mode::valid_nonce_length(size_t length) const
+ {
+ if(length == 0)
+ return false;
+ if(block_size() == 16)
+ return length < 16;
+ else
+ return length < (block_size() - 1);
+ }
+
+std::string OCB_Mode::name() const
+ {
+ return m_cipher->name() + "/OCB"; // include tag size?
+ }
+
+size_t OCB_Mode::update_granularity() const
+ {
+ return (m_par_blocks * block_size());
+ }
+
+Key_Length_Specification OCB_Mode::key_spec() const
+ {
+ return m_cipher->key_spec();
+ }
+
+void OCB_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_cipher->set_key(key, length);
+ m_L.reset(new L_computer(*m_cipher));
+ }
+
+void OCB_Mode::set_associated_data(const uint8_t ad[], size_t ad_len)
+ {
+ verify_key_set(m_L != nullptr);
+ m_ad_hash = ocb_hash(*m_L, *m_cipher, ad, ad_len);
+ }
+
+const secure_vector<uint8_t>&
+OCB_Mode::update_nonce(const uint8_t nonce[], size_t nonce_len)
+ {
+ const size_t BS = block_size();
+
+ BOTAN_ASSERT(BS == 16 || BS == 24 || BS == 32 || BS == 64,
+ "OCB block size is supported");
+
+ const size_t MASKLEN = (BS == 16 ? 6 : ((BS == 24) ? 7 : 8));
+
+ const uint8_t BOTTOM_MASK =
+ static_cast<uint8_t>((static_cast<uint16_t>(1) << MASKLEN) - 1);
+
+ m_nonce_buf.resize(BS);
+ clear_mem(&m_nonce_buf[0], m_nonce_buf.size());
+
+ copy_mem(&m_nonce_buf[BS - nonce_len], nonce, nonce_len);
+ m_nonce_buf[0] = static_cast<uint8_t>(((tag_size()*8) % (BS*8)) << (BS <= 16 ? 1 : 0));
+
+ m_nonce_buf[BS - nonce_len - 1] ^= 1;
+
+ const uint8_t bottom = m_nonce_buf[BS-1] & BOTTOM_MASK;
+ m_nonce_buf[BS-1] &= ~BOTTOM_MASK;
+
+ const bool need_new_stretch = (m_last_nonce != m_nonce_buf);
+
+ if(need_new_stretch)
+ {
+ m_last_nonce = m_nonce_buf;
+
+ m_cipher->encrypt(m_nonce_buf);
+
+ /*
+ The loop bounds (BS vs BS/2) are derived from the relation
+ between the block size and the MASKLEN. Using the terminology
+ of draft-krovetz-ocb-wide, we have to derive enough bits in
+ ShiftedKtop to read up to BLOCKLEN+bottom bits from Stretch.
+
+ +----------+---------+-------+---------+
+ | BLOCKLEN | RESIDUE | SHIFT | MASKLEN |
+ +----------+---------+-------+---------+
+ | 32 | 141 | 17 | 4 |
+ | 64 | 27 | 25 | 5 |
+ | 96 | 1601 | 33 | 6 |
+ | 128 | 135 | 8 | 6 |
+ | 192 | 135 | 40 | 7 |
+ | 256 | 1061 | 1 | 8 |
+ | 384 | 4109 | 80 | 8 |
+ | 512 | 293 | 176 | 8 |
+ | 1024 | 524355 | 352 | 9 |
+ +----------+---------+-------+---------+
+ */
+ if(BS == 16)
+ {
+ for(size_t i = 0; i != BS / 2; ++i)
+ m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i+1]);
+ }
+ else if(BS == 24)
+ {
+ for(size_t i = 0; i != 16; ++i)
+ m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i+5]);
+ }
+ else if(BS == 32)
+ {
+ for(size_t i = 0; i != BS; ++i)
+ m_nonce_buf.push_back(m_nonce_buf[i] ^ (m_nonce_buf[i] << 1) ^ (m_nonce_buf[i+1] >> 7));
+ }
+ else if(BS == 64)
+ {
+ for(size_t i = 0; i != BS / 2; ++i)
+ m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i+22]);
+ }
+
+ m_stretch = m_nonce_buf;
+ }
+
+ // now set the offset from stretch and bottom
+ const size_t shift_bytes = bottom / 8;
+ const size_t shift_bits = bottom % 8;
+
+ BOTAN_ASSERT(m_stretch.size() >= BS + shift_bytes + 1, "Size ok");
+
+ m_offset.resize(BS);
+ for(size_t i = 0; i != BS; ++i)
+ {
+ m_offset[i] = (m_stretch[i+shift_bytes] << shift_bits);
+ m_offset[i] |= (m_stretch[i+shift_bytes+1] >> (8-shift_bits));
+ }
+
+ return m_offset;
+ }
+
+void OCB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ verify_key_set(m_L != nullptr);
+
+ m_L->init(update_nonce(nonce, nonce_len));
+ zeroise(m_checksum);
+ m_block_index = 0;
+ }
+
+void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks)
+ {
+ verify_key_set(m_L != nullptr);
+ BOTAN_STATE_CHECK(m_L->initialized());
+
+ const size_t BS = block_size();
+
+ while(blocks)
+ {
+ const size_t proc_blocks = std::min(blocks, par_blocks());
+ const size_t proc_bytes = proc_blocks * BS;
+
+ const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
+
+ xor_buf(m_checksum.data(), buffer, proc_bytes);
+
+ m_cipher->encrypt_n_xex(buffer, offsets, proc_blocks);
+
+ buffer += proc_bytes;
+ blocks -= proc_blocks;
+ m_block_index += proc_blocks;
+ }
+ }
+
+size_t OCB_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_ASSERT(sz % update_granularity() == 0, "Invalid OCB input size");
+ encrypt(buf, sz / block_size());
+ return sz;
+ }
+
+void OCB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ verify_key_set(m_L != nullptr);
+
+ const size_t BS = block_size();
+
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ secure_vector<uint8_t> mac(BS);
+
+ if(sz)
+ {
+ const size_t final_full_blocks = sz / BS;
+ const size_t remainder_bytes = sz - (final_full_blocks * BS);
+
+ encrypt(buf, final_full_blocks);
+ mac = m_L->offset();
+
+ if(remainder_bytes)
+ {
+ BOTAN_ASSERT(remainder_bytes < BS, "Only a partial block left");
+ uint8_t* remainder = &buf[sz - remainder_bytes];
+
+ xor_buf(m_checksum.data(), remainder, remainder_bytes);
+ m_checksum[remainder_bytes] ^= 0x80;
+
+ // Offset_*
+ mac ^= m_L->star();
+
+ secure_vector<uint8_t> pad(BS);
+ m_cipher->encrypt(mac, pad);
+ xor_buf(remainder, pad.data(), remainder_bytes);
+ }
+ }
+ else
+ {
+ mac = m_L->offset();
+ }
+
+ // now compute the tag
+
+ // fold checksum
+ for(size_t i = 0; i != m_checksum.size(); i += BS)
+ {
+ xor_buf(mac.data(), m_checksum.data() + i, BS);
+ }
+
+ xor_buf(mac.data(), m_L->dollar().data(), BS);
+ m_cipher->encrypt(mac);
+ xor_buf(mac.data(), m_ad_hash.data(), BS);
+
+ buffer += std::make_pair(mac.data(), tag_size());
+
+ zeroise(m_checksum);
+ m_block_index = 0;
+ }
+
+void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks)
+ {
+ verify_key_set(m_L != nullptr);
+ BOTAN_STATE_CHECK(m_L->initialized());
+
+ const size_t BS = block_size();
+
+ while(blocks)
+ {
+ const size_t proc_blocks = std::min(blocks, par_blocks());
+ const size_t proc_bytes = proc_blocks * BS;
+
+ const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
+
+ m_cipher->decrypt_n_xex(buffer, offsets, proc_blocks);
+
+ xor_buf(m_checksum.data(), buffer, proc_bytes);
+
+ buffer += proc_bytes;
+ blocks -= proc_blocks;
+ m_block_index += proc_blocks;
+ }
+ }
+
+size_t OCB_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_ASSERT(sz % update_granularity() == 0, "Invalid OCB input size");
+ decrypt(buf, sz / block_size());
+ return sz;
+ }
+
+void OCB_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ verify_key_set(m_L != nullptr);
+
+ const size_t BS = block_size();
+
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ BOTAN_ASSERT(sz >= tag_size(), "We have the tag");
+
+ const size_t remaining = sz - tag_size();
+
+ secure_vector<uint8_t> mac(BS);
+
+ if(remaining)
+ {
+ const size_t final_full_blocks = remaining / BS;
+ const size_t final_bytes = remaining - (final_full_blocks * BS);
+
+ decrypt(buf, final_full_blocks);
+ mac ^= m_L->offset();
+
+ if(final_bytes)
+ {
+ BOTAN_ASSERT(final_bytes < BS, "Only a partial block left");
+
+ uint8_t* remainder = &buf[remaining - final_bytes];
+
+ mac ^= m_L->star();
+ secure_vector<uint8_t> pad(BS);
+ m_cipher->encrypt(mac, pad); // P_*
+ xor_buf(remainder, pad.data(), final_bytes);
+
+ xor_buf(m_checksum.data(), remainder, final_bytes);
+ m_checksum[final_bytes] ^= 0x80;
+ }
+ }
+ else
+ mac = m_L->offset();
+
+ // compute the mac
+
+ // fold checksum
+ for(size_t i = 0; i != m_checksum.size(); i += BS)
+ {
+ xor_buf(mac.data(), m_checksum.data() + i, BS);
+ }
+
+ mac ^= m_L->dollar();
+ m_cipher->encrypt(mac);
+ mac ^= m_ad_hash;
+
+ // reset state
+ zeroise(m_checksum);
+ m_block_index = 0;
+
+ // compare mac
+ const uint8_t* included_tag = &buf[remaining];
+
+ if(!constant_time_compare(mac.data(), included_tag, tag_size()))
+ throw Invalid_Authentication_Tag("OCB tag check failed");
+
+ // remove tag from end of message
+ buffer.resize(remaining + offset);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.h b/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.h
new file mode 100644
index 0000000000..4029fcda25
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.h
@@ -0,0 +1,137 @@
+/*
+* OCB Mode
+* (C) 2013,2014 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_OCB_H_
+#define BOTAN_AEAD_OCB_H_
+
+#include <botan/aead.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(ocb.h)
+
+namespace Botan {
+
+class BlockCipher;
+class L_computer;
+
+/**
+* OCB Mode (base class for OCB_Encryption and OCB_Decryption). Note
+* that OCB is patented, but is freely licensed in some circumstances.
+*
+* @see "The OCB Authenticated-Encryption Algorithm" RFC 7253
+* https://tools.ietf.org/html/rfc7253
+* @see "OCB For Block Ciphers Without 128-Bit Blocks"
+* (draft-krovetz-ocb-wide-d3) for the extension of OCB to
+* block ciphers with larger block sizes.
+* @see Free Licenses http://www.cs.ucdavis.edu/~rogaway/ocb/license.htm
+* @see OCB home page http://www.cs.ucdavis.edu/~rogaway/ocb
+*/
+class BOTAN_PUBLIC_API(2,0) OCB_Mode : public AEAD_Mode
+ {
+ public:
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+ std::string name() const override;
+
+ size_t update_granularity() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ bool valid_nonce_length(size_t) const override;
+
+ size_t tag_size() const override { return m_tag_size; }
+
+ void clear() override;
+
+ void reset() override;
+
+ ~OCB_Mode();
+ protected:
+ /**
+ * @param cipher the block cipher to use
+ * @param tag_size is how big the auth tag will be
+ */
+ OCB_Mode(BlockCipher* cipher, size_t tag_size);
+
+ size_t block_size() const { return m_block_size; }
+ size_t par_blocks() const { return m_par_blocks; }
+ size_t par_bytes() const { return m_checksum.size(); }
+
+ // fixme make these private
+ std::unique_ptr<BlockCipher> m_cipher;
+ std::unique_ptr<L_computer> m_L;
+
+ size_t m_block_index = 0;
+
+ secure_vector<uint8_t> m_checksum;
+ secure_vector<uint8_t> m_ad_hash;
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ const secure_vector<uint8_t>& update_nonce(const uint8_t nonce[], size_t nonce_len);
+
+ const size_t m_tag_size;
+ const size_t m_block_size;
+ const size_t m_par_blocks;
+ secure_vector<uint8_t> m_last_nonce;
+ secure_vector<uint8_t> m_stretch;
+ secure_vector<uint8_t> m_nonce_buf;
+ secure_vector<uint8_t> m_offset;
+ };
+
+class BOTAN_PUBLIC_API(2,0) OCB_Encryption final : public OCB_Mode
+ {
+ public:
+ /**
+ * @param cipher the block cipher to use
+ * @param tag_size is how big the auth tag will be
+ */
+ OCB_Encryption(BlockCipher* cipher, size_t tag_size = 16) :
+ OCB_Mode(cipher, tag_size) {}
+
+ size_t output_length(size_t input_length) const override
+ { return input_length + tag_size(); }
+
+ size_t minimum_final_size() const override { return 0; }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ private:
+ void encrypt(uint8_t input[], size_t blocks);
+ };
+
+class BOTAN_PUBLIC_API(2,0) OCB_Decryption final : public OCB_Mode
+ {
+ public:
+ /**
+ * @param cipher the block cipher to use
+ * @param tag_size is how big the auth tag will be
+ */
+ OCB_Decryption(BlockCipher* cipher, size_t tag_size = 16) :
+ OCB_Mode(cipher, tag_size) {}
+
+ size_t output_length(size_t input_length) const override
+ {
+ BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+ return input_length - tag_size();
+ }
+
+ size_t minimum_final_size() const override { return tag_size(); }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ private:
+ void decrypt(uint8_t input[], size_t blocks);
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/aead/siv/info.txt b/comm/third_party/botan/src/lib/modes/aead/siv/info.txt
new file mode 100644
index 0000000000..4894896665
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/siv/info.txt
@@ -0,0 +1,9 @@
+<defines>
+AEAD_SIV -> 20131202
+</defines>
+
+<requires>
+cmac
+ctr
+poly_dbl
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/aead/siv/siv.cpp b/comm/third_party/botan/src/lib/modes/aead/siv/siv.cpp
new file mode 100644
index 0000000000..8bd82a4684
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/siv/siv.cpp
@@ -0,0 +1,211 @@
+/*
+* SIV Mode Encryption
+* (C) 2013,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/siv.h>
+#include <botan/block_cipher.h>
+#include <botan/cmac.h>
+#include <botan/internal/poly_dbl.h>
+#include <botan/ctr.h>
+
+namespace Botan {
+
+SIV_Mode::SIV_Mode(BlockCipher* cipher) :
+ m_name(cipher->name() + "/SIV"),
+ m_ctr(new CTR_BE(cipher->clone(), 8)),
+ m_mac(new CMAC(cipher)),
+ m_bs(cipher->block_size())
+ {
+ // Not really true but only 128 bit allowed at the moment
+ if(m_bs != 16)
+ throw Invalid_Argument("SIV requires a 128 bit block cipher");
+ }
+
+SIV_Mode::~SIV_Mode()
+ {
+ // for ~unique_ptr
+ }
+
+void SIV_Mode::clear()
+ {
+ m_ctr->clear();
+ m_mac->clear();
+ reset();
+ }
+
+void SIV_Mode::reset()
+ {
+ m_nonce.clear();
+ m_msg_buf.clear();
+ m_ad_macs.clear();
+ }
+
+std::string SIV_Mode::name() const
+ {
+ return m_name;
+ }
+
+bool SIV_Mode::valid_nonce_length(size_t) const
+ {
+ return true;
+ }
+
+size_t SIV_Mode::update_granularity() const
+ {
+ /*
+ This value does not particularly matter as regardless SIV_Mode::update
+ buffers all input, so in theory this could be 1. However as for instance
+ Transform_Filter creates update_granularity() uint8_t buffers, use a
+ somewhat large size to avoid bouncing on a tiny buffer.
+ */
+ return 128;
+ }
+
+Key_Length_Specification SIV_Mode::key_spec() const
+ {
+ return m_mac->key_spec().multiple(2);
+ }
+
+void SIV_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ const size_t keylen = length / 2;
+ m_mac->set_key(key, keylen);
+ m_ctr->set_key(key + keylen, keylen);
+ m_ad_macs.clear();
+ }
+
+size_t SIV_Mode::maximum_associated_data_inputs() const
+ {
+ return block_size() * 8 - 2;
+ }
+
+void SIV_Mode::set_associated_data_n(size_t n, const uint8_t ad[], size_t length)
+ {
+ const size_t max_ads = maximum_associated_data_inputs();
+ if(n > max_ads)
+ throw Invalid_Argument(name() + " allows no more than " + std::to_string(max_ads) + " ADs");
+
+ if(n >= m_ad_macs.size())
+ m_ad_macs.resize(n+1);
+
+ m_ad_macs[n] = m_mac->process(ad, length);
+ }
+
+void SIV_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ if(nonce_len)
+ m_nonce = m_mac->process(nonce, nonce_len);
+ else
+ m_nonce.clear();
+
+ m_msg_buf.clear();
+ }
+
+size_t SIV_Mode::process(uint8_t buf[], size_t sz)
+ {
+ // all output is saved for processing in finish
+ m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
+ return 0;
+ }
+
+secure_vector<uint8_t> SIV_Mode::S2V(const uint8_t* text, size_t text_len)
+ {
+ const std::vector<uint8_t> zeros(block_size());
+
+ secure_vector<uint8_t> V = m_mac->process(zeros.data(), zeros.size());
+
+ for(size_t i = 0; i != m_ad_macs.size(); ++i)
+ {
+ poly_double_n(V.data(), V.size());
+ V ^= m_ad_macs[i];
+ }
+
+ if(m_nonce.size())
+ {
+ poly_double_n(V.data(), V.size());
+ V ^= m_nonce;
+ }
+
+ if(text_len < block_size())
+ {
+ poly_double_n(V.data(), V.size());
+ xor_buf(V.data(), text, text_len);
+ V[text_len] ^= 0x80;
+ return m_mac->process(V);
+ }
+
+ m_mac->update(text, text_len - block_size());
+ xor_buf(V.data(), &text[text_len - block_size()], block_size());
+ m_mac->update(V);
+
+ return m_mac->final();
+ }
+
+void SIV_Mode::set_ctr_iv(secure_vector<uint8_t> V)
+ {
+ V[m_bs-8] &= 0x7F;
+ V[m_bs-4] &= 0x7F;
+
+ ctr().set_iv(V.data(), V.size());
+ }
+
+void SIV_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+
+ buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
+ msg_buf().clear();
+
+ const secure_vector<uint8_t> V = S2V(buffer.data() + offset, buffer.size() - offset);
+
+ buffer.insert(buffer.begin() + offset, V.begin(), V.end());
+
+ if(buffer.size() != offset + V.size())
+ {
+ set_ctr_iv(V);
+ ctr().cipher1(&buffer[offset + V.size()], buffer.size() - offset - V.size());
+ }
+ }
+
+void SIV_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+
+ if(msg_buf().size() > 0)
+ {
+ buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
+ msg_buf().clear();
+ }
+
+ const size_t sz = buffer.size() - offset;
+
+ BOTAN_ASSERT(sz >= tag_size(), "We have the tag");
+
+ secure_vector<uint8_t> V(buffer.data() + offset,
+ buffer.data() + offset + block_size());
+
+ if(buffer.size() != offset + V.size())
+ {
+ set_ctr_iv(V);
+
+ ctr().cipher(buffer.data() + offset + V.size(),
+ buffer.data() + offset,
+ buffer.size() - offset - V.size());
+ }
+
+ const secure_vector<uint8_t> T = S2V(buffer.data() + offset, buffer.size() - offset - V.size());
+
+ if(!constant_time_compare(T.data(), V.data(), T.size()))
+ throw Invalid_Authentication_Tag("SIV tag check failed");
+
+ buffer.resize(buffer.size() - tag_size());
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/siv/siv.h b/comm/third_party/botan/src/lib/modes/aead/siv/siv.h
new file mode 100644
index 0000000000..c76fd32296
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/siv/siv.h
@@ -0,0 +1,129 @@
+/*
+* SIV Mode
+* (C) 2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_SIV_H_
+#define BOTAN_AEAD_SIV_H_
+
+#include <botan/aead.h>
+#include <botan/stream_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(siv.h)
+
+namespace Botan {
+
+class BlockCipher;
+class MessageAuthenticationCode;
+
+/**
+* Base class for SIV encryption and decryption (@see RFC 5297)
+*/
+class BOTAN_PUBLIC_API(2,0) SIV_Mode : public AEAD_Mode
+ {
+ public:
+ size_t process(uint8_t buf[], size_t size) override;
+
+ /**
+ * Sets the nth element of the vector of associated data
+ * @param n index into the AD vector
+ * @param ad associated data
+ * @param ad_len length of associated data in bytes
+ */
+ void set_associated_data_n(size_t n, const uint8_t ad[], size_t ad_len) override;
+
+ size_t maximum_associated_data_inputs() const override;
+
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override
+ {
+ set_associated_data_n(0, ad, ad_len);
+ }
+
+ std::string name() const override;
+
+ size_t update_granularity() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ bool valid_nonce_length(size_t) const override;
+
+ void clear() override;
+
+ void reset() override;
+
+ size_t tag_size() const override { return 16; }
+
+ ~SIV_Mode();
+
+ protected:
+ explicit SIV_Mode(BlockCipher* cipher);
+
+ size_t block_size() const { return m_bs; }
+
+ StreamCipher& ctr() { return *m_ctr; }
+
+ void set_ctr_iv(secure_vector<uint8_t> V);
+
+ secure_vector<uint8_t>& msg_buf() { return m_msg_buf; }
+
+ secure_vector<uint8_t> S2V(const uint8_t text[], size_t text_len);
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ const std::string m_name;
+ std::unique_ptr<StreamCipher> m_ctr;
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+ secure_vector<uint8_t> m_nonce, m_msg_buf;
+ std::vector<secure_vector<uint8_t>> m_ad_macs;
+ const size_t m_bs;
+ };
+
+/**
+* SIV Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) SIV_Encryption final : public SIV_Mode
+ {
+ public:
+ /**
+ * @param cipher a block cipher
+ */
+ explicit SIV_Encryption(BlockCipher* cipher) : SIV_Mode(cipher) {}
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override
+ { return input_length + tag_size(); }
+
+ size_t minimum_final_size() const override { return 0; }
+ };
+
+/**
+* SIV Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) SIV_Decryption final : public SIV_Mode
+ {
+ public:
+ /**
+ * @param cipher a 128-bit block cipher
+ */
+ explicit SIV_Decryption(BlockCipher* cipher) : SIV_Mode(cipher) {}
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override
+ {
+ BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+ return input_length - tag_size();
+ }
+
+ size_t minimum_final_size() const override { return tag_size(); }
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/cbc/cbc.cpp b/comm/third_party/botan/src/lib/modes/cbc/cbc.cpp
new file mode 100644
index 0000000000..cde08f64a9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cbc/cbc.cpp
@@ -0,0 +1,323 @@
+/*
+* CBC Mode
+* (C) 1999-2007,2013,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cbc.h>
+#include <botan/mode_pad.h>
+#include <botan/internal/rounding.h>
+
+namespace Botan {
+
+CBC_Mode::CBC_Mode(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) :
+ m_cipher(cipher),
+ m_padding(padding),
+ m_block_size(cipher->block_size())
+ {
+ if(m_padding && !m_padding->valid_blocksize(m_block_size))
+ throw Invalid_Argument("Padding " + m_padding->name() +
+ " cannot be used with " +
+ cipher->name() + "/CBC");
+ }
+
+void CBC_Mode::clear()
+ {
+ m_cipher->clear();
+ reset();
+ }
+
+void CBC_Mode::reset()
+ {
+ m_state.clear();
+ }
+
+std::string CBC_Mode::name() const
+ {
+ if(m_padding)
+ return cipher().name() + "/CBC/" + padding().name();
+ else
+ return cipher().name() + "/CBC/CTS";
+ }
+
+size_t CBC_Mode::update_granularity() const
+ {
+ return cipher().parallel_bytes();
+ }
+
+Key_Length_Specification CBC_Mode::key_spec() const
+ {
+ return cipher().key_spec();
+ }
+
+size_t CBC_Mode::default_nonce_length() const
+ {
+ return block_size();
+ }
+
+bool CBC_Mode::valid_nonce_length(size_t n) const
+ {
+ return (n == 0 || n == block_size());
+ }
+
+void CBC_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_cipher->set_key(key, length);
+ m_state.clear();
+ }
+
+void CBC_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ /*
+ * A nonce of zero length means carry the last ciphertext value over
+ * as the new IV, as unfortunately some protocols require this. If
+ * this is the first message then we use an IV of all zeros.
+ */
+ if(nonce_len)
+ m_state.assign(nonce, nonce + nonce_len);
+ else if(m_state.empty())
+ m_state.resize(m_cipher->block_size());
+ // else leave the state alone
+ }
+
+size_t CBC_Encryption::minimum_final_size() const
+ {
+ return 0;
+ }
+
+size_t CBC_Encryption::output_length(size_t input_length) const
+ {
+ if(input_length == 0)
+ return block_size();
+ else
+ return round_up(input_length, block_size());
+ }
+
+size_t CBC_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(state().empty() == false);
+ const size_t BS = block_size();
+
+ BOTAN_ASSERT(sz % BS == 0, "CBC input is full blocks");
+ const size_t blocks = sz / BS;
+
+ if(blocks > 0)
+ {
+ xor_buf(&buf[0], state_ptr(), BS);
+ cipher().encrypt(&buf[0]);
+
+ for(size_t i = 1; i != blocks; ++i)
+ {
+ xor_buf(&buf[BS*i], &buf[BS*(i-1)], BS);
+ cipher().encrypt(&buf[BS*i]);
+ }
+
+ state().assign(&buf[BS*(blocks-1)], &buf[BS*blocks]);
+ }
+
+ return sz;
+ }
+
+void CBC_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_STATE_CHECK(state().empty() == false);
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+
+ const size_t BS = block_size();
+
+ const size_t bytes_in_final_block = (buffer.size()-offset) % BS;
+
+ padding().add_padding(buffer, bytes_in_final_block, BS);
+
+ BOTAN_ASSERT_EQUAL(buffer.size() % BS, offset % BS, "Padded to block boundary");
+
+ update(buffer, offset);
+ }
+
+bool CTS_Encryption::valid_nonce_length(size_t n) const
+ {
+ return (n == block_size());
+ }
+
+size_t CTS_Encryption::minimum_final_size() const
+ {
+ return block_size() + 1;
+ }
+
+size_t CTS_Encryption::output_length(size_t input_length) const
+ {
+ return input_length; // no ciphertext expansion in CTS
+ }
+
+void CTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_STATE_CHECK(state().empty() == false);
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ uint8_t* buf = buffer.data() + offset;
+ const size_t sz = buffer.size() - offset;
+
+ const size_t BS = block_size();
+
+ if(sz < BS + 1)
+ throw Encoding_Error(name() + ": insufficient data to encrypt");
+
+ if(sz % BS == 0)
+ {
+ update(buffer, offset);
+
+ // swap last two blocks
+ for(size_t i = 0; i != BS; ++i)
+ std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]);
+ }
+ else
+ {
+ const size_t full_blocks = ((sz / BS) - 1) * BS;
+ const size_t final_bytes = sz - full_blocks;
+ BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
+
+ secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
+ buffer.resize(full_blocks + offset);
+ update(buffer, offset);
+
+ xor_buf(last.data(), state_ptr(), BS);
+ cipher().encrypt(last.data());
+
+ for(size_t i = 0; i != final_bytes - BS; ++i)
+ {
+ last[i] ^= last[i + BS];
+ last[i + BS] ^= last[i];
+ }
+
+ cipher().encrypt(last.data());
+
+ buffer += last;
+ }
+ }
+
+size_t CBC_Decryption::output_length(size_t input_length) const
+ {
+ return input_length; // precise for CTS, worst case otherwise
+ }
+
+size_t CBC_Decryption::minimum_final_size() const
+ {
+ return block_size();
+ }
+
+size_t CBC_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(state().empty() == false);
+
+ const size_t BS = block_size();
+
+ BOTAN_ASSERT(sz % BS == 0, "Input is full blocks");
+ size_t blocks = sz / BS;
+
+ while(blocks)
+ {
+ const size_t to_proc = std::min(BS * blocks, m_tempbuf.size());
+
+ cipher().decrypt_n(buf, m_tempbuf.data(), to_proc / BS);
+
+ xor_buf(m_tempbuf.data(), state_ptr(), BS);
+ xor_buf(&m_tempbuf[BS], buf, to_proc - BS);
+ copy_mem(state_ptr(), buf + (to_proc - BS), BS);
+
+ copy_mem(buf, m_tempbuf.data(), to_proc);
+
+ buf += to_proc;
+ blocks -= to_proc / BS;
+ }
+
+ return sz;
+ }
+
+void CBC_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_STATE_CHECK(state().empty() == false);
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+
+ const size_t BS = block_size();
+
+ if(sz == 0 || sz % BS)
+ throw Decoding_Error(name() + ": Ciphertext not a multiple of block size");
+
+ update(buffer, offset);
+
+ const size_t pad_bytes = BS - padding().unpad(&buffer[buffer.size()-BS], BS);
+ buffer.resize(buffer.size() - pad_bytes); // remove padding
+ if(pad_bytes == 0 && padding().name() != "NoPadding")
+ {
+ throw Decoding_Error("Invalid CBC padding");
+ }
+ }
+
+void CBC_Decryption::reset()
+ {
+ CBC_Mode::reset();
+ zeroise(m_tempbuf);
+ }
+
+bool CTS_Decryption::valid_nonce_length(size_t n) const
+ {
+ return (n == block_size());
+ }
+
+size_t CTS_Decryption::minimum_final_size() const
+ {
+ return block_size() + 1;
+ }
+
+void CTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_STATE_CHECK(state().empty() == false);
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ const size_t BS = block_size();
+
+ if(sz < BS + 1)
+ throw Encoding_Error(name() + ": insufficient data to decrypt");
+
+ if(sz % BS == 0)
+ {
+ // swap last two blocks
+
+ for(size_t i = 0; i != BS; ++i)
+ std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]);
+
+ update(buffer, offset);
+ }
+ else
+ {
+ const size_t full_blocks = ((sz / BS) - 1) * BS;
+ const size_t final_bytes = sz - full_blocks;
+ BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
+
+ secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
+ buffer.resize(full_blocks + offset);
+ update(buffer, offset);
+
+ cipher().decrypt(last.data());
+
+ xor_buf(last.data(), &last[BS], final_bytes - BS);
+
+ for(size_t i = 0; i != final_bytes - BS; ++i)
+ std::swap(last[i], last[i + BS]);
+
+ cipher().decrypt(last.data());
+ xor_buf(last.data(), state_ptr(), BS);
+
+ buffer += last;
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/cbc/cbc.h b/comm/third_party/botan/src/lib/modes/cbc/cbc.h
new file mode 100644
index 0000000000..7a488dbd50
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cbc/cbc.h
@@ -0,0 +1,157 @@
+/*
+* CBC mode
+* (C) 1999-2007,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODE_CBC_H_
+#define BOTAN_MODE_CBC_H_
+
+#include <botan/cipher_mode.h>
+#include <botan/block_cipher.h>
+#include <botan/mode_pad.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cbc.h)
+
+namespace Botan {
+
+/**
+* CBC Mode
+*/
+class BOTAN_PUBLIC_API(2,0) CBC_Mode : public Cipher_Mode
+ {
+ public:
+ std::string name() const override;
+
+ size_t update_granularity() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ size_t default_nonce_length() const override;
+
+ bool valid_nonce_length(size_t n) const override;
+
+ void clear() override;
+
+ void reset() override;
+
+ protected:
+ CBC_Mode(BlockCipher* cipher, BlockCipherModePaddingMethod* padding);
+
+ const BlockCipher& cipher() const { return *m_cipher; }
+
+ const BlockCipherModePaddingMethod& padding() const
+ {
+ BOTAN_ASSERT_NONNULL(m_padding);
+ return *m_padding;
+ }
+
+ size_t block_size() const { return m_block_size; }
+
+ secure_vector<uint8_t>& state() { return m_state; }
+
+ uint8_t* state_ptr() { return m_state.data(); }
+
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ std::unique_ptr<BlockCipher> m_cipher;
+ std::unique_ptr<BlockCipherModePaddingMethod> m_padding;
+ secure_vector<uint8_t> m_state;
+ size_t m_block_size;
+ };
+
+/**
+* CBC Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) CBC_Encryption : public CBC_Mode
+ {
+ public:
+ /**
+ * @param cipher block cipher to use
+ * @param padding padding method to use
+ */
+ CBC_Encryption(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) :
+ CBC_Mode(cipher, padding) {}
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override;
+
+ size_t minimum_final_size() const override;
+ };
+
+/**
+* CBC Encryption with ciphertext stealing (CBC-CS3 variant)
+*/
+class BOTAN_PUBLIC_API(2,0) CTS_Encryption final : public CBC_Encryption
+ {
+ public:
+ /**
+ * @param cipher block cipher to use
+ */
+ explicit CTS_Encryption(BlockCipher* cipher) : CBC_Encryption(cipher, nullptr) {}
+
+ size_t output_length(size_t input_length) const override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t minimum_final_size() const override;
+
+ bool valid_nonce_length(size_t n) const override;
+ };
+
+/**
+* CBC Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) CBC_Decryption : public CBC_Mode
+ {
+ public:
+ /**
+ * @param cipher block cipher to use
+ * @param padding padding method to use
+ */
+ CBC_Decryption(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) :
+ CBC_Mode(cipher, padding), m_tempbuf(update_granularity()) {}
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override;
+
+ size_t minimum_final_size() const override;
+
+ void reset() override;
+
+ private:
+ secure_vector<uint8_t> m_tempbuf;
+ };
+
+/**
+* CBC Decryption with ciphertext stealing (CBC-CS3 variant)
+*/
+class BOTAN_PUBLIC_API(2,0) CTS_Decryption final : public CBC_Decryption
+ {
+ public:
+ /**
+ * @param cipher block cipher to use
+ */
+ explicit CTS_Decryption(BlockCipher* cipher) : CBC_Decryption(cipher, nullptr) {}
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t minimum_final_size() const override;
+
+ bool valid_nonce_length(size_t n) const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/cbc/info.txt b/comm/third_party/botan/src/lib/modes/cbc/info.txt
new file mode 100644
index 0000000000..778ba1e252
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cbc/info.txt
@@ -0,0 +1,8 @@
+<defines>
+MODE_CBC -> 20131128
+</defines>
+
+<requires>
+block
+mode_pad
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/cfb/cfb.cpp b/comm/third_party/botan/src/lib/modes/cfb/cfb.cpp
new file mode 100644
index 0000000000..0c619e322c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cfb/cfb.cpp
@@ -0,0 +1,229 @@
+/*
+* CFB Mode
+* (C) 1999-2007,2013,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cfb.h>
+
+namespace Botan {
+
+CFB_Mode::CFB_Mode(BlockCipher* cipher, size_t feedback_bits) :
+ m_cipher(cipher),
+ m_block_size(m_cipher->block_size()),
+ m_feedback_bytes(feedback_bits ? feedback_bits / 8 : m_block_size)
+ {
+ if(feedback_bits % 8 || feedback() > cipher->block_size())
+ throw Invalid_Argument(name() + ": feedback bits " +
+ std::to_string(feedback_bits) + " not supported");
+ }
+
+void CFB_Mode::clear()
+ {
+ m_cipher->clear();
+ m_keystream.clear();
+ reset();
+ }
+
+void CFB_Mode::reset()
+ {
+ m_state.clear();
+ zeroise(m_keystream);
+ }
+
+std::string CFB_Mode::name() const
+ {
+ if(feedback() == cipher().block_size())
+ return cipher().name() + "/CFB";
+ else
+ return cipher().name() + "/CFB(" + std::to_string(feedback()*8) + ")";
+ }
+
+size_t CFB_Mode::output_length(size_t input_length) const
+ {
+ return input_length;
+ }
+
+size_t CFB_Mode::update_granularity() const
+ {
+ return feedback();
+ }
+
+size_t CFB_Mode::minimum_final_size() const
+ {
+ return 0;
+ }
+
+Key_Length_Specification CFB_Mode::key_spec() const
+ {
+ return cipher().key_spec();
+ }
+
+size_t CFB_Mode::default_nonce_length() const
+ {
+ return block_size();
+ }
+
+bool CFB_Mode::valid_nonce_length(size_t n) const
+ {
+ return (n == 0 || n == block_size());
+ }
+
+void CFB_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_cipher->set_key(key, length);
+ m_keystream.resize(m_cipher->block_size());
+ }
+
+void CFB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ verify_key_set(!m_keystream.empty());
+
+ if(nonce_len == 0)
+ {
+ if(m_state.empty())
+ {
+ throw Invalid_State("CFB requires a non-empty initial nonce");
+ }
+ // No reason to encrypt state->keystream_buf, because no change
+ }
+ else
+ {
+ m_state.assign(nonce, nonce + nonce_len);
+ cipher().encrypt(m_state, m_keystream);
+ m_keystream_pos = 0;
+ }
+ }
+
+void CFB_Mode::shift_register()
+ {
+ const size_t shift = feedback();
+ const size_t carryover = block_size() - shift;
+
+ if(carryover > 0)
+ {
+ copy_mem(m_state.data(), &m_state[shift], carryover);
+ }
+ copy_mem(&m_state[carryover], m_keystream.data(), shift);
+ cipher().encrypt(m_state, m_keystream);
+ m_keystream_pos = 0;
+ }
+
+size_t CFB_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ verify_key_set(!m_keystream.empty());
+ BOTAN_STATE_CHECK(m_state.empty() == false);
+
+ const size_t shift = feedback();
+
+ size_t left = sz;
+
+ if(m_keystream_pos != 0)
+ {
+ const size_t take = std::min<size_t>(left, shift - m_keystream_pos);
+
+ xor_buf(m_keystream.data() + m_keystream_pos, buf, take);
+ copy_mem(buf, m_keystream.data() + m_keystream_pos, take);
+
+ m_keystream_pos += take;
+ left -= take;
+ buf += take;
+
+ if(m_keystream_pos == shift)
+ {
+ shift_register();
+ }
+ }
+
+ while(left >= shift)
+ {
+ xor_buf(m_keystream.data(), buf, shift);
+ copy_mem(buf, m_keystream.data(), shift);
+
+ left -= shift;
+ buf += shift;
+ shift_register();
+ }
+
+ if(left > 0)
+ {
+ xor_buf(m_keystream.data(), buf, left);
+ copy_mem(buf, m_keystream.data(), left);
+ m_keystream_pos += left;
+ }
+
+ return sz;
+ }
+
+void CFB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ update(buffer, offset);
+ }
+
+namespace {
+
+inline void xor_copy(uint8_t buf[], uint8_t key_buf[], size_t len)
+ {
+ for(size_t i = 0; i != len; ++i)
+ {
+ uint8_t k = key_buf[i];
+ key_buf[i] = buf[i];
+ buf[i] ^= k;
+ }
+ }
+
+}
+
+size_t CFB_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ verify_key_set(!m_keystream.empty());
+ BOTAN_STATE_CHECK(m_state.empty() == false);
+
+ const size_t shift = feedback();
+
+ size_t left = sz;
+
+ if(m_keystream_pos != 0)
+ {
+ const size_t take = std::min<size_t>(left, shift - m_keystream_pos);
+
+ xor_copy(buf, m_keystream.data() + m_keystream_pos, take);
+
+ m_keystream_pos += take;
+ left -= take;
+ buf += take;
+
+ if(m_keystream_pos == shift)
+ {
+ shift_register();
+ }
+ }
+
+ while(left >= shift)
+ {
+ xor_copy(buf, m_keystream.data(), shift);
+ left -= shift;
+ buf += shift;
+ shift_register();
+ }
+
+ if(left > 0)
+ {
+ xor_copy(buf, m_keystream.data(), left);
+ m_keystream_pos += left;
+ }
+
+ return sz;
+ }
+
+void CFB_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ update(buffer, offset);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/cfb/cfb.h b/comm/third_party/botan/src/lib/modes/cfb/cfb.h
new file mode 100644
index 0000000000..1f9e554872
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cfb/cfb.h
@@ -0,0 +1,106 @@
+/*
+* CFB mode
+* (C) 1999-2007,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODE_CFB_H_
+#define BOTAN_MODE_CFB_H_
+
+#include <botan/cipher_mode.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cfb.h)
+
+namespace Botan {
+
+/**
+* CFB Mode
+*/
+class BOTAN_PUBLIC_API(2,0) CFB_Mode : public Cipher_Mode
+ {
+ public:
+ std::string name() const override final;
+
+ size_t update_granularity() const override final;
+
+ size_t minimum_final_size() const override final;
+
+ Key_Length_Specification key_spec() const override final;
+
+ size_t output_length(size_t input_length) const override final;
+
+ size_t default_nonce_length() const override final;
+
+ bool valid_nonce_length(size_t n) const override final;
+
+ void clear() override final;
+
+ void reset() override final;
+ protected:
+ CFB_Mode(BlockCipher* cipher, size_t feedback_bits);
+
+ void shift_register();
+
+ size_t feedback() const { return m_feedback_bytes; }
+ const BlockCipher& cipher() const { return *m_cipher; }
+ size_t block_size() const { return m_block_size; }
+
+ secure_vector<uint8_t> m_state;
+ secure_vector<uint8_t> m_keystream;
+ size_t m_keystream_pos = 0;
+
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ std::unique_ptr<BlockCipher> m_cipher;
+ const size_t m_block_size;
+ const size_t m_feedback_bytes;
+ };
+
+/**
+* CFB Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) CFB_Encryption final : public CFB_Mode
+ {
+ public:
+ /**
+ * If feedback_bits is zero, cipher->block_size() bytes will be used.
+ * @param cipher block cipher to use
+ * @param feedback_bits number of bits fed back into the shift register,
+ * must be a multiple of 8
+ */
+ CFB_Encryption(BlockCipher* cipher, size_t feedback_bits) :
+ CFB_Mode(cipher, feedback_bits) {}
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+/**
+* CFB Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) CFB_Decryption final : public CFB_Mode
+ {
+ public:
+ /**
+ * If feedback_bits is zero, cipher->block_size() bytes will be used.
+ * @param cipher block cipher to use
+ * @param feedback_bits number of bits fed back into the shift register,
+ * must be a multiple of 8
+ */
+ CFB_Decryption(BlockCipher* cipher, size_t feedback_bits) :
+ CFB_Mode(cipher, feedback_bits) {}
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/cfb/info.txt b/comm/third_party/botan/src/lib/modes/cfb/info.txt
new file mode 100644
index 0000000000..77a6af448e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cfb/info.txt
@@ -0,0 +1,7 @@
+<defines>
+MODE_CFB -> 20131128
+</defines>
+
+<requires>
+block
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/cipher_mode.cpp b/comm/third_party/botan/src/lib/modes/cipher_mode.cpp
new file mode 100644
index 0000000000..710f16ba22
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cipher_mode.cpp
@@ -0,0 +1,205 @@
+/*
+* Cipher Modes
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cipher_mode.h>
+#include <botan/stream_mode.h>
+#include <botan/scan_name.h>
+#include <botan/parsing.h>
+#include <sstream>
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+ #include <botan/block_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_MODES)
+ #include <botan/aead.h>
+#endif
+
+#if defined(BOTAN_HAS_MODE_CBC)
+ #include <botan/cbc.h>
+#endif
+
+#if defined(BOTAN_HAS_MODE_CFB)
+ #include <botan/cfb.h>
+#endif
+
+#if defined(BOTAN_HAS_MODE_XTS)
+ #include <botan/xts.h>
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+ #include <botan/internal/openssl.h>
+#endif
+
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+ #include <botan/internal/commoncrypto.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<Cipher_Mode> Cipher_Mode::create_or_throw(const std::string& algo,
+ Cipher_Dir direction,
+ const std::string& provider)
+ {
+ if(auto mode = Cipher_Mode::create(algo, direction, provider))
+ return mode;
+
+ throw Lookup_Error("Cipher mode", algo, provider);
+ }
+
+std::unique_ptr<Cipher_Mode> Cipher_Mode::create(const std::string& algo,
+ Cipher_Dir direction,
+ const std::string& provider)
+ {
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+ if(provider.empty() || provider == "commoncrypto")
+ {
+ std::unique_ptr<Cipher_Mode> commoncrypto_cipher(make_commoncrypto_cipher_mode(algo, direction));
+
+ if(commoncrypto_cipher)
+ return commoncrypto_cipher;
+
+ if(!provider.empty())
+ return std::unique_ptr<Cipher_Mode>();
+ }
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider.empty() || provider == "openssl")
+ {
+ std::unique_ptr<Cipher_Mode> openssl_cipher(make_openssl_cipher_mode(algo, direction));
+
+ if(openssl_cipher)
+ return openssl_cipher;
+
+ if(!provider.empty())
+ return std::unique_ptr<Cipher_Mode>();
+ }
+#endif
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+ if(auto sc = StreamCipher::create(algo))
+ {
+ return std::unique_ptr<Cipher_Mode>(new Stream_Cipher_Mode(sc.release()));
+ }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_MODES)
+ if(auto aead = AEAD_Mode::create(algo, direction))
+ {
+ return std::unique_ptr<Cipher_Mode>(aead.release());
+ }
+#endif
+
+ if(algo.find('/') != std::string::npos)
+ {
+ const std::vector<std::string> algo_parts = split_on(algo, '/');
+ const std::string cipher_name = algo_parts[0];
+ const std::vector<std::string> mode_info = parse_algorithm_name(algo_parts[1]);
+
+ if(mode_info.empty())
+ return std::unique_ptr<Cipher_Mode>();
+
+ std::ostringstream alg_args;
+
+ alg_args << '(' << cipher_name;
+ for(size_t i = 1; i < mode_info.size(); ++i)
+ alg_args << ',' << mode_info[i];
+ for(size_t i = 2; i < algo_parts.size(); ++i)
+ alg_args << ',' << algo_parts[i];
+ alg_args << ')';
+
+ const std::string mode_name = mode_info[0] + alg_args.str();
+ return Cipher_Mode::create(mode_name, direction, provider);
+ }
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+
+ SCAN_Name spec(algo);
+
+ if(spec.arg_count() == 0)
+ {
+ return std::unique_ptr<Cipher_Mode>();
+ }
+
+ std::unique_ptr<BlockCipher> bc(BlockCipher::create(spec.arg(0), provider));
+
+ if(!bc)
+ {
+ return std::unique_ptr<Cipher_Mode>();
+ }
+
+#if defined(BOTAN_HAS_MODE_CBC)
+ if(spec.algo_name() == "CBC")
+ {
+ const std::string padding = spec.arg(1, "PKCS7");
+
+ if(padding == "CTS")
+ {
+ if(direction == ENCRYPTION)
+ return std::unique_ptr<Cipher_Mode>(new CTS_Encryption(bc.release()));
+ else
+ return std::unique_ptr<Cipher_Mode>(new CTS_Decryption(bc.release()));
+ }
+ else
+ {
+ std::unique_ptr<BlockCipherModePaddingMethod> pad(get_bc_pad(padding));
+
+ if(pad)
+ {
+ if(direction == ENCRYPTION)
+ return std::unique_ptr<Cipher_Mode>(new CBC_Encryption(bc.release(), pad.release()));
+ else
+ return std::unique_ptr<Cipher_Mode>(new CBC_Decryption(bc.release(), pad.release()));
+ }
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_MODE_XTS)
+ if(spec.algo_name() == "XTS")
+ {
+ if(direction == ENCRYPTION)
+ return std::unique_ptr<Cipher_Mode>(new XTS_Encryption(bc.release()));
+ else
+ return std::unique_ptr<Cipher_Mode>(new XTS_Decryption(bc.release()));
+ }
+#endif
+
+#if defined(BOTAN_HAS_MODE_CFB)
+ if(spec.algo_name() == "CFB")
+ {
+ const size_t feedback_bits = spec.arg_as_integer(1, 8*bc->block_size());
+ if(direction == ENCRYPTION)
+ return std::unique_ptr<Cipher_Mode>(new CFB_Encryption(bc.release(), feedback_bits));
+ else
+ return std::unique_ptr<Cipher_Mode>(new CFB_Decryption(bc.release(), feedback_bits));
+ }
+#endif
+
+#endif
+
+ return std::unique_ptr<Cipher_Mode>();
+ }
+
+//static
+std::vector<std::string> Cipher_Mode::providers(const std::string& algo_spec)
+ {
+ const std::vector<std::string>& possible = { "base", "openssl", "commoncrypto" };
+ std::vector<std::string> providers;
+ for(auto&& prov : possible)
+ {
+ std::unique_ptr<Cipher_Mode> mode = Cipher_Mode::create(algo_spec, ENCRYPTION, prov);
+ if(mode)
+ {
+ providers.push_back(prov); // available
+ }
+ }
+ return providers;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/cipher_mode.h b/comm/third_party/botan/src/lib/modes/cipher_mode.h
new file mode 100644
index 0000000000..9bf0b6811e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cipher_mode.h
@@ -0,0 +1,198 @@
+/*
+* Cipher Modes
+* (C) 2013,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CIPHER_MODE_H_
+#define BOTAN_CIPHER_MODE_H_
+
+#include <botan/secmem.h>
+#include <botan/sym_algo.h>
+#include <botan/exceptn.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+/**
+* The two possible directions for cipher filters, determining whether they
+* actually perform encryption or decryption.
+*/
+enum Cipher_Dir : int { ENCRYPTION, DECRYPTION };
+
+/**
+* Interface for cipher modes
+*/
+class BOTAN_PUBLIC_API(2,0) Cipher_Mode : public SymmetricAlgorithm
+ {
+ public:
+ /**
+ * @return list of available providers for this algorithm, empty if not available
+ * @param algo_spec algorithm name
+ */
+ static std::vector<std::string> providers(const std::string& algo_spec);
+
+ /**
+ * Create an AEAD mode
+ * @param algo the algorithm to create
+ * @param direction specify if this should be an encryption or decryption AEAD
+ * @param provider optional specification for provider to use
+ * @return an AEAD mode or a null pointer if not available
+ */
+ static std::unique_ptr<Cipher_Mode> create(const std::string& algo,
+ Cipher_Dir direction,
+ const std::string& provider = "");
+
+ /**
+ * Create an AEAD mode, or throw
+ * @param algo the algorithm to create
+ * @param direction specify if this should be an encryption or decryption AEAD
+ * @param provider optional specification for provider to use
+ * @return an AEAD mode, or throw an exception
+ */
+ static std::unique_ptr<Cipher_Mode> create_or_throw(const std::string& algo,
+ Cipher_Dir direction,
+ const std::string& provider = "");
+
+ /*
+ * Prepare for processing a message under the specified nonce
+ */
+ virtual void start_msg(const uint8_t nonce[], size_t nonce_len) = 0;
+
+ /**
+ * Begin processing a message.
+ * @param nonce the per message nonce
+ */
+ template<typename Alloc>
+ void start(const std::vector<uint8_t, Alloc>& nonce)
+ {
+ start_msg(nonce.data(), nonce.size());
+ }
+
+ /**
+ * Begin processing a message.
+ * @param nonce the per message nonce
+ * @param nonce_len length of nonce
+ */
+ void start(const uint8_t nonce[], size_t nonce_len)
+ {
+ start_msg(nonce, nonce_len);
+ }
+
+ /**
+ * Begin processing a message.
+ */
+ void start()
+ {
+ return start_msg(nullptr, 0);
+ }
+
+ /**
+ * Process message blocks
+ *
+ * Input must be a multiple of update_granularity
+ *
+ * Processes msg in place and returns bytes written. Normally
+ * this will be either msg_len (indicating the entire message was
+ * processed) or for certain AEAD modes zero (indicating that the
+ * mode requires the entire message be processed in one pass).
+ *
+ * @param msg the message to be processed
+ * @param msg_len length of the message in bytes
+ */
+ virtual size_t process(uint8_t msg[], size_t msg_len) = 0;
+
+ /**
+ * Process some data. Input must be in size update_granularity() uint8_t blocks.
+ * @param buffer in/out parameter which will possibly be resized
+ * @param offset an offset into blocks to begin processing
+ */
+ void update(secure_vector<uint8_t>& buffer, size_t offset = 0)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset ok");
+ uint8_t* buf = buffer.data() + offset;
+ const size_t buf_size = buffer.size() - offset;
+
+ const size_t written = process(buf, buf_size);
+ buffer.resize(offset + written);
+ }
+
+ /**
+ * Complete processing of a message.
+ *
+ * @param final_block in/out parameter which must be at least
+ * minimum_final_size() bytes, and will be set to any final output
+ * @param offset an offset into final_block to begin processing
+ */
+ virtual void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) = 0;
+
+ /**
+ * Returns the size of the output if this transform is used to process a
+ * message with input_length bytes. In most cases the answer is precise.
+ * If it is not possible to precise (namely for CBC decryption) instead a
+ * lower bound is returned.
+ */
+ virtual size_t output_length(size_t input_length) const = 0;
+
+ /**
+ * @return size of required blocks to update
+ */
+ virtual size_t update_granularity() const = 0;
+
+ /**
+ * @return required minimium size to finalize() - may be any
+ * length larger than this.
+ */
+ virtual size_t minimum_final_size() const = 0;
+
+ /**
+ * @return the default size for a nonce
+ */
+ virtual size_t default_nonce_length() const = 0;
+
+ /**
+ * @return true iff nonce_len is a valid length for the nonce
+ */
+ virtual bool valid_nonce_length(size_t nonce_len) const = 0;
+
+ /**
+ * Resets just the message specific state and allows encrypting again under the existing key
+ */
+ virtual void reset() = 0;
+
+ /**
+ * @return true iff this mode provides authentication as well as
+ * confidentiality.
+ */
+ virtual bool authenticated() const { return false; }
+
+ /**
+ * @return the size of the authentication tag used (in bytes)
+ */
+ virtual size_t tag_size() const { return 0; }
+
+ /**
+ * @return provider information about this implementation. Default is "base",
+ * might also return "sse2", "avx2", "openssl", or some other arbitrary string.
+ */
+ virtual std::string provider() const { return "base"; }
+ };
+
+/**
+* Get a cipher mode by name (eg "AES-128/CBC" or "Serpent/XTS")
+* @param algo_spec cipher name
+* @param direction ENCRYPTION or DECRYPTION
+* @param provider provider implementation to choose
+*/
+inline Cipher_Mode* get_cipher_mode(const std::string& algo_spec,
+ Cipher_Dir direction,
+ const std::string& provider = "")
+ {
+ return Cipher_Mode::create(algo_spec, direction, provider).release();
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/info.txt b/comm/third_party/botan/src/lib/modes/info.txt
new file mode 100644
index 0000000000..4c19db04ca
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/info.txt
@@ -0,0 +1,9 @@
+<defines>
+MODES -> 20150626
+CIPHER_MODES -> 20180124
+</defines>
+
+<header:public>
+cipher_mode.h
+stream_mode.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/modes/mode_pad/info.txt b/comm/third_party/botan/src/lib/modes/mode_pad/info.txt
new file mode 100644
index 0000000000..12b6e5b3a9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/mode_pad/info.txt
@@ -0,0 +1,3 @@
+<defines>
+CIPHER_MODE_PADDING -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp b/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp
new file mode 100644
index 0000000000..18bb71af5d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp
@@ -0,0 +1,333 @@
+/*
+* CBC Padding Methods
+* (C) 1999-2007,2013,2018,2020 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/mode_pad.h>
+#include <botan/exceptn.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+/**
+* Get a block cipher padding method by name
+*/
+BlockCipherModePaddingMethod* get_bc_pad(const std::string& algo_spec)
+ {
+ if(algo_spec == "NoPadding")
+ return new Null_Padding;
+
+ if(algo_spec == "PKCS7")
+ return new PKCS7_Padding;
+
+ if(algo_spec == "OneAndZeros")
+ return new OneAndZeros_Padding;
+
+ if(algo_spec == "X9.23")
+ return new ANSI_X923_Padding;
+
+ if(algo_spec == "ESP")
+ return new ESP_Padding;
+
+ return nullptr;
+ }
+
+/*
+* Pad with PKCS #7 Method
+*/
+void PKCS7_Padding::add_padding(secure_vector<uint8_t>& buffer,
+ size_t last_byte_pos,
+ size_t BS) const
+ {
+ /*
+ Padding format is
+ 01
+ 0202
+ 030303
+ ...
+ */
+ BOTAN_DEBUG_ASSERT(last_byte_pos < BS);
+
+ const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos);
+
+ buffer.resize(buffer.size() + padding_len);
+
+ CT::poison(&last_byte_pos, 1);
+ CT::poison(buffer.data(), buffer.size());
+
+ BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0);
+ BOTAN_DEBUG_ASSERT(buffer.size() >= BS);
+
+ const size_t start_of_last_block = buffer.size() - BS;
+ const size_t end_of_last_block = buffer.size();
+ const size_t start_of_padding = buffer.size() - padding_len;
+
+ for(size_t i = start_of_last_block; i != end_of_last_block; ++i)
+ {
+ auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding));
+ buffer[i] = needs_padding.select(padding_len, buffer[i]);
+ }
+
+ CT::unpoison(buffer.data(), buffer.size());
+ CT::unpoison(last_byte_pos);
+ }
+
+/*
+* Unpad with PKCS #7 Method
+*/
+size_t PKCS7_Padding::unpad(const uint8_t input[], size_t input_length) const
+ {
+ if(!valid_blocksize(input_length))
+ return input_length;
+
+ CT::poison(input, input_length);
+
+ const uint8_t last_byte = input[input_length-1];
+
+ /*
+ The input should == the block size so if the last byte exceeds
+ that then the padding is certainly invalid
+ */
+ auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length);
+
+ const size_t pad_pos = input_length - last_byte;
+
+ for(size_t i = 0; i != input_length - 1; ++i)
+ {
+ // Does this byte equal the expected pad byte?
+ const auto pad_eq = CT::Mask<size_t>::is_equal(input[i], last_byte);
+
+ // Ignore values that are not part of the padding
+ const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos);
+ bad_input |= in_range & (~pad_eq);
+ }
+
+ CT::unpoison(input, input_length);
+
+ return bad_input.select_and_unpoison(input_length, pad_pos);
+ }
+
+/*
+* Pad with ANSI X9.23 Method
+*/
+void ANSI_X923_Padding::add_padding(secure_vector<uint8_t>& buffer,
+ size_t last_byte_pos,
+ size_t BS) const
+ {
+ /*
+ Padding format is
+ 01
+ 0002
+ 000003
+ ...
+ */
+ BOTAN_DEBUG_ASSERT(last_byte_pos < BS);
+
+ const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos);
+
+ buffer.resize(buffer.size() + padding_len);
+
+ CT::poison(&last_byte_pos, 1);
+ CT::poison(buffer.data(), buffer.size());
+
+ BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0);
+ BOTAN_DEBUG_ASSERT(buffer.size() >= BS);
+
+ const size_t start_of_last_block = buffer.size() - BS;
+ const size_t end_of_zero_padding = buffer.size() - 1;
+ const size_t start_of_padding = buffer.size() - padding_len;
+
+ for(size_t i = start_of_last_block; i != end_of_zero_padding; ++i)
+ {
+ auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding));
+ buffer[i] = needs_padding.select(0, buffer[i]);
+ }
+
+ buffer[buffer.size()-1] = padding_len;
+ CT::unpoison(buffer.data(), buffer.size());
+ CT::unpoison(last_byte_pos);
+ }
+
+/*
+* Unpad with ANSI X9.23 Method
+*/
+size_t ANSI_X923_Padding::unpad(const uint8_t input[], size_t input_length) const
+ {
+ if(!valid_blocksize(input_length))
+ return input_length;
+
+ CT::poison(input, input_length);
+
+ const size_t last_byte = input[input_length-1];
+
+ auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length);
+
+ const size_t pad_pos = input_length - last_byte;
+
+ for(size_t i = 0; i != input_length - 1; ++i)
+ {
+ // Ignore values that are not part of the padding
+ const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos);
+ const auto pad_is_nonzero = CT::Mask<size_t>::expand(input[i]);
+ bad_input |= pad_is_nonzero & in_range;
+ }
+
+ CT::unpoison(input, input_length);
+
+ return bad_input.select_and_unpoison(input_length, pad_pos);
+ }
+
+/*
+* Pad with One and Zeros Method
+*/
+void OneAndZeros_Padding::add_padding(secure_vector<uint8_t>& buffer,
+ size_t last_byte_pos,
+ size_t BS) const
+ {
+ /*
+ Padding format is
+ 80
+ 8000
+ 800000
+ ...
+ */
+
+ BOTAN_DEBUG_ASSERT(last_byte_pos < BS);
+
+ const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos);
+
+ buffer.resize(buffer.size() + padding_len);
+
+ CT::poison(&last_byte_pos, 1);
+ CT::poison(buffer.data(), buffer.size());
+
+ BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0);
+ BOTAN_DEBUG_ASSERT(buffer.size() >= BS);
+
+ const size_t start_of_last_block = buffer.size() - BS;
+ const size_t end_of_last_block = buffer.size();
+ const size_t start_of_padding = buffer.size() - padding_len;
+
+ for(size_t i = start_of_last_block; i != end_of_last_block; ++i)
+ {
+ auto needs_80 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_equal(i, start_of_padding));
+ auto needs_00 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gt(i, start_of_padding));
+ buffer[i] = needs_00.select(0x00, needs_80.select(0x80, buffer[i]));
+ }
+
+ CT::unpoison(buffer.data(), buffer.size());
+ CT::unpoison(last_byte_pos);
+ }
+
+/*
+* Unpad with One and Zeros Method
+*/
+size_t OneAndZeros_Padding::unpad(const uint8_t input[], size_t input_length) const
+ {
+ if(!valid_blocksize(input_length))
+ return input_length;
+
+ CT::poison(input, input_length);
+
+ auto bad_input = CT::Mask<uint8_t>::cleared();
+ auto seen_0x80 = CT::Mask<uint8_t>::cleared();
+
+ size_t pad_pos = input_length - 1;
+ size_t i = input_length;
+
+ while(i)
+ {
+ const auto is_0x80 = CT::Mask<uint8_t>::is_equal(input[i-1], 0x80);
+ const auto is_zero = CT::Mask<uint8_t>::is_zero(input[i-1]);
+
+ seen_0x80 |= is_0x80;
+ pad_pos -= seen_0x80.if_not_set_return(1);
+ bad_input |= ~seen_0x80 & ~is_zero;
+ i--;
+ }
+ bad_input |= ~seen_0x80;
+
+ CT::unpoison(input, input_length);
+
+ return CT::Mask<size_t>::expand(bad_input).select_and_unpoison(input_length, pad_pos);
+ }
+
+/*
+* Pad with ESP Padding Method
+*/
+void ESP_Padding::add_padding(secure_vector<uint8_t>& buffer,
+ size_t last_byte_pos,
+ size_t BS) const
+ {
+ /*
+ Padding format is
+ 01
+ 0102
+ 010203
+ ...
+ */
+ BOTAN_DEBUG_ASSERT(last_byte_pos < BS);
+
+ const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos);
+
+ buffer.resize(buffer.size() + padding_len);
+
+ CT::poison(&last_byte_pos, 1);
+ CT::poison(buffer.data(), buffer.size());
+
+ BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0);
+ BOTAN_DEBUG_ASSERT(buffer.size() >= BS);
+
+ const size_t start_of_last_block = buffer.size() - BS;
+ const size_t end_of_last_block = buffer.size();
+ const size_t start_of_padding = buffer.size() - padding_len;
+
+ uint8_t pad_ctr = 0x01;
+
+ for(size_t i = start_of_last_block; i != end_of_last_block; ++i)
+ {
+ auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding));
+ buffer[i] = needs_padding.select(pad_ctr, buffer[i]);
+ pad_ctr = needs_padding.select(pad_ctr + 1, pad_ctr);
+ }
+
+ CT::unpoison(buffer.data(), buffer.size());
+ CT::unpoison(last_byte_pos);
+ }
+
+/*
+* Unpad with ESP Padding Method
+*/
+size_t ESP_Padding::unpad(const uint8_t input[], size_t input_length) const
+ {
+ if(!valid_blocksize(input_length))
+ return input_length;
+
+ CT::poison(input, input_length);
+
+ const uint8_t input_length_8 = static_cast<uint8_t>(input_length);
+ const uint8_t last_byte = input[input_length-1];
+
+ auto bad_input = CT::Mask<uint8_t>::is_zero(last_byte) |
+ CT::Mask<uint8_t>::is_gt(last_byte, input_length_8);
+
+ const uint8_t pad_pos = input_length_8 - last_byte;
+ size_t i = input_length_8 - 1;
+ while(i)
+ {
+ const auto in_range = CT::Mask<size_t>::is_gt(i, pad_pos);
+ const auto incrementing = CT::Mask<uint8_t>::is_equal(input[i-1], input[i]-1);
+
+ bad_input |= CT::Mask<uint8_t>(in_range) & ~incrementing;
+ --i;
+ }
+
+ CT::unpoison(input, input_length);
+ return bad_input.select_and_unpoison(input_length_8, pad_pos);
+ }
+
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.h b/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.h
new file mode 100644
index 0000000000..b0e4a3cfae
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.h
@@ -0,0 +1,160 @@
+/*
+* CBC Padding Methods
+* (C) 1999-2008,2013 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODE_PADDING_H_
+#define BOTAN_MODE_PADDING_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(mode_pad.h)
+
+namespace Botan {
+
+/**
+* Block Cipher Mode Padding Method
+* This class is pretty limited, it cannot deal well with
+* randomized padding methods, or any padding method that
+* wants to add more than one block. For instance, it should
+* be possible to define cipher text stealing mode as simply
+* a padding mode for CBC, which happens to consume the last
+* two block (and requires use of the block cipher).
+*/
+class BOTAN_PUBLIC_API(2,0) BlockCipherModePaddingMethod
+ {
+ public:
+ /**
+ * Add padding bytes to buffer.
+ * @param buffer data to pad
+ * @param final_block_bytes size of the final block in bytes
+ * @param block_size size of each block in bytes
+ */
+ virtual void add_padding(secure_vector<uint8_t>& buffer,
+ size_t final_block_bytes,
+ size_t block_size) const = 0;
+
+ /**
+ * Remove padding bytes from block
+ * @param block the last block
+ * @param len the size of the block in bytes
+ * @return number of data bytes, or if the padding is invalid returns len
+ */
+ virtual size_t unpad(const uint8_t block[], size_t len) const = 0;
+
+ /**
+ * @param block_size of the cipher
+ * @return valid block size for this padding mode
+ */
+ virtual bool valid_blocksize(size_t block_size) const = 0;
+
+ /**
+ * @return name of the mode
+ */
+ virtual std::string name() const = 0;
+
+ /**
+ * virtual destructor
+ */
+ virtual ~BlockCipherModePaddingMethod() = default;
+ };
+
+/**
+* PKCS#7 Padding
+*/
+class BOTAN_PUBLIC_API(2,0) PKCS7_Padding final : public BlockCipherModePaddingMethod
+ {
+ public:
+ void add_padding(secure_vector<uint8_t>& buffer,
+ size_t final_block_bytes,
+ size_t block_size) const override;
+
+ size_t unpad(const uint8_t[], size_t) const override;
+
+ bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); }
+
+ std::string name() const override { return "PKCS7"; }
+ };
+
+/**
+* ANSI X9.23 Padding
+*/
+class BOTAN_PUBLIC_API(2,0) ANSI_X923_Padding final : public BlockCipherModePaddingMethod
+ {
+ public:
+ void add_padding(secure_vector<uint8_t>& buffer,
+ size_t final_block_bytes,
+ size_t block_size) const override;
+
+ size_t unpad(const uint8_t[], size_t) const override;
+
+ bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); }
+
+ std::string name() const override { return "X9.23"; }
+ };
+
+/**
+* One And Zeros Padding (ISO/IEC 9797-1, padding method 2)
+*/
+class BOTAN_PUBLIC_API(2,0) OneAndZeros_Padding final : public BlockCipherModePaddingMethod
+ {
+ public:
+ void add_padding(secure_vector<uint8_t>& buffer,
+ size_t final_block_bytes,
+ size_t block_size) const override;
+
+ size_t unpad(const uint8_t[], size_t) const override;
+
+ bool valid_blocksize(size_t bs) const override { return (bs > 2); }
+
+ std::string name() const override { return "OneAndZeros"; }
+ };
+
+/**
+* ESP Padding (RFC 4304)
+*/
+class BOTAN_PUBLIC_API(2,0) ESP_Padding final : public BlockCipherModePaddingMethod
+ {
+ public:
+ void add_padding(secure_vector<uint8_t>& buffer,
+ size_t final_block_bytes,
+ size_t block_size) const override;
+
+ size_t unpad(const uint8_t[], size_t) const override;
+
+ bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); }
+
+ std::string name() const override { return "ESP"; }
+ };
+
+/**
+* Null Padding
+*/
+class BOTAN_PUBLIC_API(2,0) Null_Padding final : public BlockCipherModePaddingMethod
+ {
+ public:
+ void add_padding(secure_vector<uint8_t>&, size_t, size_t) const override
+ {
+ /* no padding */
+ }
+
+ size_t unpad(const uint8_t[], size_t size) const override { return size; }
+
+ bool valid_blocksize(size_t) const override { return true; }
+
+ std::string name() const override { return "NoPadding"; }
+ };
+
+/**
+* Get a block cipher padding mode by name (eg "NoPadding" or "PKCS7")
+* @param algo_spec block cipher padding mode name
+*/
+BOTAN_PUBLIC_API(2,0) BlockCipherModePaddingMethod* get_bc_pad(const std::string& algo_spec);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/stream_mode.h b/comm/third_party/botan/src/lib/modes/stream_mode.h
new file mode 100644
index 0000000000..da3fc38cf0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/stream_mode.h
@@ -0,0 +1,84 @@
+/*
+* (C) 2015 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STREAM_MODE_H_
+#define BOTAN_STREAM_MODE_H_
+
+#include <botan/cipher_mode.h>
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+ #include <botan/stream_cipher.h>
+#endif
+
+BOTAN_FUTURE_INTERNAL_HEADER(stream_mode.h)
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+
+class BOTAN_PUBLIC_API(2,0) Stream_Cipher_Mode final : public Cipher_Mode
+ {
+ public:
+ /**
+ * @param cipher underyling stream cipher
+ */
+ explicit Stream_Cipher_Mode(StreamCipher* cipher) : m_cipher(cipher) {}
+
+ size_t process(uint8_t buf[], size_t sz) override
+ {
+ m_cipher->cipher1(buf, sz);
+ return sz;
+ }
+
+ void finish(secure_vector<uint8_t>& buf, size_t offset) override
+ { return update(buf, offset); }
+
+ size_t output_length(size_t input_length) const override { return input_length; }
+
+ size_t update_granularity() const override { return 1; }
+
+ size_t minimum_final_size() const override { return 0; }
+
+ size_t default_nonce_length() const override { return 0; }
+
+ bool valid_nonce_length(size_t nonce_len) const override
+ { return m_cipher->valid_iv_length(nonce_len); }
+
+ Key_Length_Specification key_spec() const override { return m_cipher->key_spec(); }
+
+ std::string name() const override { return m_cipher->name(); }
+
+ void clear() override
+ {
+ m_cipher->clear();
+ reset();
+ }
+
+ void reset() override { /* no msg state */ }
+
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override
+ {
+ if(nonce_len > 0)
+ {
+ m_cipher->set_iv(nonce, nonce_len);
+ }
+ }
+
+ void key_schedule(const uint8_t key[], size_t length) override
+ {
+ m_cipher->set_key(key, length);
+ }
+
+ std::unique_ptr<StreamCipher> m_cipher;
+ };
+
+#endif
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/xts/info.txt b/comm/third_party/botan/src/lib/modes/xts/info.txt
new file mode 100644
index 0000000000..cee850be78
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/xts/info.txt
@@ -0,0 +1,8 @@
+<defines>
+MODE_XTS -> 20131128
+</defines>
+
+<requires>
+block
+poly_dbl
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/xts/xts.cpp b/comm/third_party/botan/src/lib/modes/xts/xts.cpp
new file mode 100644
index 0000000000..559584b082
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/xts/xts.cpp
@@ -0,0 +1,248 @@
+/*
+* XTS Mode
+* (C) 2009,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/xts.h>
+#include <botan/internal/poly_dbl.h>
+
+namespace Botan {
+
+XTS_Mode::XTS_Mode(BlockCipher* cipher) :
+ m_cipher(cipher),
+ m_cipher_block_size(m_cipher->block_size()),
+ m_cipher_parallelism(m_cipher->parallel_bytes())
+ {
+ if(poly_double_supported_size(m_cipher_block_size) == false)
+ {
+ throw Invalid_Argument("Cannot use " + cipher->name() + " with XTS");
+ }
+
+ m_tweak_cipher.reset(m_cipher->clone());
+ }
+
+void XTS_Mode::clear()
+ {
+ m_cipher->clear();
+ m_tweak_cipher->clear();
+ reset();
+ }
+
+void XTS_Mode::reset()
+ {
+ m_tweak.clear();
+ }
+
+std::string XTS_Mode::name() const
+ {
+ return cipher().name() + "/XTS";
+ }
+
+size_t XTS_Mode::minimum_final_size() const
+ {
+ return cipher_block_size();
+ }
+
+Key_Length_Specification XTS_Mode::key_spec() const
+ {
+ return cipher().key_spec().multiple(2);
+ }
+
+size_t XTS_Mode::default_nonce_length() const
+ {
+ return cipher_block_size();
+ }
+
+bool XTS_Mode::valid_nonce_length(size_t n) const
+ {
+ return cipher_block_size() == n;
+ }
+
+void XTS_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ const size_t key_half = length / 2;
+
+ if(length % 2 == 1 || !m_cipher->valid_keylength(key_half))
+ throw Invalid_Key_Length(name(), length);
+
+ m_cipher->set_key(key, key_half);
+ m_tweak_cipher->set_key(&key[key_half], key_half);
+ }
+
+void XTS_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ m_tweak.resize(update_granularity());
+ copy_mem(m_tweak.data(), nonce, nonce_len);
+ m_tweak_cipher->encrypt(m_tweak.data());
+
+ update_tweak(0);
+ }
+
+void XTS_Mode::update_tweak(size_t which)
+ {
+ const size_t BS = m_tweak_cipher->block_size();
+
+ if(which > 0)
+ poly_double_n_le(m_tweak.data(), &m_tweak[(which-1)*BS], BS);
+
+ const size_t blocks_in_tweak = update_granularity() / BS;
+
+ for(size_t i = 1; i < blocks_in_tweak; ++i)
+ poly_double_n_le(&m_tweak[i*BS], &m_tweak[(i-1)*BS], BS);
+ }
+
+size_t XTS_Encryption::output_length(size_t input_length) const
+ {
+ return input_length;
+ }
+
+size_t XTS_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(tweak_set());
+ const size_t BS = cipher_block_size();
+
+ BOTAN_ASSERT(sz % BS == 0, "Input is full blocks");
+ size_t blocks = sz / BS;
+
+ const size_t blocks_in_tweak = update_granularity() / BS;
+
+ while(blocks)
+ {
+ const size_t to_proc = std::min(blocks, blocks_in_tweak);
+
+ cipher().encrypt_n_xex(buf, tweak(), to_proc);
+
+ buf += to_proc * BS;
+ blocks -= to_proc;
+
+ update_tweak(to_proc);
+ }
+
+ return sz;
+ }
+
+void XTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ BOTAN_ASSERT(sz >= minimum_final_size(), "Have sufficient final input in XTS encrypt");
+
+ const size_t BS = cipher_block_size();
+
+ if(sz % BS == 0)
+ {
+ update(buffer, offset);
+ }
+ else
+ {
+ // steal ciphertext
+ const size_t full_blocks = ((sz / BS) - 1) * BS;
+ const size_t final_bytes = sz - full_blocks;
+ BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
+
+ secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
+ buffer.resize(full_blocks + offset);
+ update(buffer, offset);
+
+ xor_buf(last, tweak(), BS);
+ cipher().encrypt(last);
+ xor_buf(last, tweak(), BS);
+
+ for(size_t i = 0; i != final_bytes - BS; ++i)
+ {
+ last[i] ^= last[i + BS];
+ last[i + BS] ^= last[i];
+ last[i] ^= last[i + BS];
+ }
+
+ xor_buf(last, tweak() + BS, BS);
+ cipher().encrypt(last);
+ xor_buf(last, tweak() + BS, BS);
+
+ buffer += last;
+ }
+ }
+
+size_t XTS_Decryption::output_length(size_t input_length) const
+ {
+ return input_length;
+ }
+
+size_t XTS_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(tweak_set());
+ const size_t BS = cipher_block_size();
+
+ BOTAN_ASSERT(sz % BS == 0, "Input is full blocks");
+ size_t blocks = sz / BS;
+
+ const size_t blocks_in_tweak = update_granularity() / BS;
+
+ while(blocks)
+ {
+ const size_t to_proc = std::min(blocks, blocks_in_tweak);
+
+ cipher().decrypt_n_xex(buf, tweak(), to_proc);
+
+ buf += to_proc * BS;
+ blocks -= to_proc;
+
+ update_tweak(to_proc);
+ }
+
+ return sz;
+ }
+
+void XTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ BOTAN_ASSERT(sz >= minimum_final_size(), "Have sufficient final input in XTS decrypt");
+
+ const size_t BS = cipher_block_size();
+
+ if(sz % BS == 0)
+ {
+ update(buffer, offset);
+ }
+ else
+ {
+ // steal ciphertext
+ const size_t full_blocks = ((sz / BS) - 1) * BS;
+ const size_t final_bytes = sz - full_blocks;
+ BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
+
+ secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
+ buffer.resize(full_blocks + offset);
+ update(buffer, offset);
+
+ xor_buf(last, tweak() + BS, BS);
+ cipher().decrypt(last);
+ xor_buf(last, tweak() + BS, BS);
+
+ for(size_t i = 0; i != final_bytes - BS; ++i)
+ {
+ last[i] ^= last[i + BS];
+ last[i + BS] ^= last[i];
+ last[i] ^= last[i + BS];
+ }
+
+ xor_buf(last, tweak(), BS);
+ cipher().decrypt(last);
+ xor_buf(last, tweak(), BS);
+
+ buffer += last;
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/xts/xts.h b/comm/third_party/botan/src/lib/modes/xts/xts.h
new file mode 100644
index 0000000000..75de93c088
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/xts/xts.h
@@ -0,0 +1,103 @@
+/*
+* XTS mode, from IEEE P1619
+* (C) 2009,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODE_XTS_H_
+#define BOTAN_MODE_XTS_H_
+
+#include <botan/cipher_mode.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(xts.h)
+
+namespace Botan {
+
+/**
+* IEEE P1619 XTS Mode
+*/
+class BOTAN_PUBLIC_API(2,0) XTS_Mode : public Cipher_Mode
+ {
+ public:
+ std::string name() const override;
+
+ size_t update_granularity() const override { return m_cipher_parallelism; }
+
+ size_t minimum_final_size() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ size_t default_nonce_length() const override;
+
+ bool valid_nonce_length(size_t n) const override;
+
+ void clear() override;
+
+ void reset() override;
+
+ protected:
+ explicit XTS_Mode(BlockCipher* cipher);
+
+ const uint8_t* tweak() const { return m_tweak.data(); }
+
+ bool tweak_set() const { return m_tweak.empty() == false; }
+
+ const BlockCipher& cipher() const { return *m_cipher; }
+
+ void update_tweak(size_t last_used);
+
+ size_t cipher_block_size() const { return m_cipher_block_size; }
+
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ std::unique_ptr<BlockCipher> m_cipher;
+ std::unique_ptr<BlockCipher> m_tweak_cipher;
+ secure_vector<uint8_t> m_tweak;
+ const size_t m_cipher_block_size;
+ const size_t m_cipher_parallelism;
+ };
+
+/**
+* IEEE P1619 XTS Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) XTS_Encryption final : public XTS_Mode
+ {
+ public:
+ /**
+ * @param cipher underlying block cipher
+ */
+ explicit XTS_Encryption(BlockCipher* cipher) : XTS_Mode(cipher) {}
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override;
+ };
+
+/**
+* IEEE P1619 XTS Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) XTS_Decryption final : public XTS_Mode
+ {
+ public:
+ /**
+ * @param cipher underlying block cipher
+ */
+ explicit XTS_Decryption(BlockCipher* cipher) : XTS_Mode(cipher) {}
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override;
+ };
+
+}
+
+#endif