aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--checks/bench.cpp4
-rw-r--r--src/aead/aead.h111
-rw-r--r--src/algo_base/transform.h86
-rw-r--r--src/engine/core_engine/core_modes.cpp8
-rw-r--r--src/filters/aead_filt/aead_filt.cpp105
-rw-r--r--src/filters/aead_filt/aead_filt.h48
-rw-r--r--src/filters/filters.h2
-rw-r--r--src/filters/info.txt2
-rw-r--r--src/filters/modes/xts/xts.cpp409
-rw-r--r--src/filters/modes/xts/xts.h91
-rw-r--r--src/filters/transform_filter.cpp99
-rw-r--r--src/filters/transform_filter.h67
-rw-r--r--src/modes/aead/aead.cpp (renamed from src/aead/aead.cpp)0
-rw-r--r--src/modes/aead/aead.h59
-rw-r--r--src/modes/aead/eax/eax.cpp (renamed from src/aead/eax/eax.cpp)0
-rw-r--r--src/modes/aead/eax/eax.h (renamed from src/aead/eax/eax.h)0
-rw-r--r--src/modes/aead/eax/info.txt (renamed from src/aead/eax/info.txt)0
-rw-r--r--src/modes/aead/gcm/gcm.cpp (renamed from src/aead/gcm/gcm.cpp)0
-rw-r--r--src/modes/aead/gcm/gcm.h (renamed from src/aead/gcm/gcm.h)0
-rw-r--r--src/modes/aead/gcm/info.txt (renamed from src/aead/gcm/info.txt)0
-rw-r--r--src/modes/aead/info.txt (renamed from src/aead/info.txt)0
-rw-r--r--src/modes/aead/ocb/info.txt (renamed from src/aead/ocb/info.txt)0
-rw-r--r--src/modes/aead/ocb/ocb.cpp (renamed from src/aead/ocb/ocb.cpp)0
-rw-r--r--src/modes/aead/ocb/ocb.h (renamed from src/aead/ocb/ocb.h)0
-rw-r--r--src/modes/info.txt (renamed from src/filters/modes/xts/info.txt)1
-rw-r--r--src/modes/xts/info.txt1
-rw-r--r--src/modes/xts/xts.cpp292
-rw-r--r--src/modes/xts/xts.h86
28 files changed, 706 insertions, 765 deletions
diff --git a/checks/bench.cpp b/checks/bench.cpp
index ee34b5233..3deff5136 100644
--- a/checks/bench.cpp
+++ b/checks/bench.cpp
@@ -196,9 +196,7 @@ bool bench_algo(const std::string& algo,
continue;
filt->set_key(Botan::SymmetricKey(&buf[0], cipher_keylen));
-
- if(filt->valid_iv_length(cipher_ivlen / 2))
- filt->set_iv(Botan::InitializationVector(&buf[0], cipher_ivlen));
+ filt->set_iv(Botan::InitializationVector(&buf[0], cipher_ivlen));
Botan::Pipe pipe(filt, new Botan::BitBucket);
pipe.start_msg();
diff --git a/src/aead/aead.h b/src/aead/aead.h
deleted file mode 100644
index 0aa50f348..000000000
--- a/src/aead/aead.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
-* Interface for AEAD modes
-* (C) 2013 Jack Lloyd
-*
-* Distributed under the terms of the Botan license
-*/
-
-#ifndef BOTAN_AEAD_MODE_H__
-#define BOTAN_AEAD_MODE_H__
-
-#include <botan/sym_algo.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 AEAD_Mode : public SymmetricAlgorithm
- {
- public:
- /**
- * Returns the size of the output if this mode is used to process
- * a message with input_length bytes. Typically this will be
- * input_length plus or minus the length of the tag.
- */
- 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 Random nonce appropriate for passing to start
- */
- //virtual secure_vector<byte> nonce(RandomNumberGenerator& rng) const = 0;
-
- /**
- * Set associated data that is not included in the ciphertext but
- * that should be authenticated. Must be called after set_key
- * and before finish.
- *
- * 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 byte ad[], size_t ad_len) = 0;
-
- template<typename Alloc>
- void set_associated_data_vec(const std::vector<byte, Alloc>& ad)
- {
- set_associated_data(&ad[0], ad.size());
- }
-
- virtual bool valid_nonce_length(size_t) const = 0;
-
- /**
- * Begin processing a message.
- *
- * @param nonce the per message nonce
- * @param nonce_len length of nonce
- */
- virtual secure_vector<byte> start(const byte nonce[], size_t nonce_len) = 0;
-
- template<typename Alloc>
- secure_vector<byte> start_vec(const std::vector<byte, Alloc>& nonce)
- {
- return start(&nonce[0], nonce.size());
- }
-
- /**
- * Update (encrypt or decrypt) some data. Input must be in size
- * update_granularity() byte blocks.
- * @param blocks in/out paramter which will possibly be resized
- */
- virtual void update(secure_vector<byte>& blocks, size_t offset = 0) = 0;
-
- /**
- * Complete processing of a message. For decryption, may throw an exception
- * due to authentication failure.
- *
- * @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<byte>& final_block, size_t offset = 0) = 0;
-
- virtual ~AEAD_Mode() {}
- };
-
-/**
-* Get an AEAD mode by name (eg "AES-128/GCM" or "Serpent/EAX")
-*/
-BOTAN_DLL AEAD_Mode* get_aead(const std::string& name, Cipher_Dir direction);
-
-}
-
-#endif
diff --git a/src/algo_base/transform.h b/src/algo_base/transform.h
new file mode 100644
index 000000000..672b39ed0
--- /dev/null
+++ b/src/algo_base/transform.h
@@ -0,0 +1,86 @@
+/*
+* Transformations of data
+* (C) 2013 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TRANSFORM_H__
+#define BOTAN_TRANSFORM_H__
+
+#include <botan/sym_algo.h>
+
+namespace Botan {
+
+/**
+* Interface for general transformations on data
+*/
+class Transformation : public SymmetricAlgorithm
+ {
+ public:
+ /**
+ * Begin processing a message.
+ * @param nonce the per message nonce
+ */
+ template<typename Alloc>
+ secure_vector<byte> start_vec(const std::vector<byte, Alloc>& nonce)
+ {
+ return start(&nonce[0], nonce.size());
+ }
+
+ /**
+ * Begin processing a message.
+ * @param nonce the per message nonce
+ * @param nonce_len length of nonce
+ */
+ virtual secure_vector<byte> start(const byte nonce[], size_t nonce_len) = 0;
+
+ /**
+ * Process some data. Input must be in size update_granularity() byte blocks.
+ * @param blocks in/out paramter which will possibly be resized
+ */
+ virtual void update(secure_vector<byte>& blocks, size_t offset = 0) = 0;
+
+ /**
+ * 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<byte>& 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. Will throw if unable to give a precise
+ * answer.
+ */
+ 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_size() 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;
+
+ virtual ~Transformation() {}
+ };
+
+}
+
+#endif
diff --git a/src/engine/core_engine/core_modes.cpp b/src/engine/core_engine/core_modes.cpp
index 199b71838..cf972dd5c 100644
--- a/src/engine/core_engine/core_modes.cpp
+++ b/src/engine/core_engine/core_modes.cpp
@@ -36,7 +36,7 @@
#include <botan/ctr.h>
#endif
-#if defined(BOTAN_HAS_XTS)
+#if defined(BOTAN_HAS_MODE_XTS)
#include <botan/xts.h>
#endif
@@ -141,13 +141,13 @@ Keyed_Filter* get_cipher_mode(const BlockCipher* block_cipher,
#endif
}
-#if defined(BOTAN_HAS_XTS)
+#if defined(BOTAN_HAS_MODE_XTS)
if(mode == "XTS")
{
if(direction == ENCRYPTION)
- return new XTS_Encryption(block_cipher->clone());
+ return new Transformation_Filter(new XTS_Encryption(block_cipher->clone()));
else
- return new XTS_Decryption(block_cipher->clone());
+ return new Transformation_Filter(new XTS_Decryption(block_cipher->clone()));
}
#endif
diff --git a/src/filters/aead_filt/aead_filt.cpp b/src/filters/aead_filt/aead_filt.cpp
deleted file mode 100644
index f70b8eafe..000000000
--- a/src/filters/aead_filt/aead_filt.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
-* Filter interface for AEAD modes
-* (C) 2013 Jack Lloyd
-*
-* Distributed under the terms of the Botan license
-*/
-
-#include <botan/aead_filt.h>
-
-namespace Botan {
-
-AEAD_Filter::AEAD_Filter(AEAD_Mode* aead) :
- Buffered_Filter(aead->update_granularity(),
- aead->minimum_final_size()),
- m_aead(aead)
- {
- }
-
-std::string AEAD_Filter::name() const
- {
- return m_aead->name();
- }
-
-void AEAD_Filter::Nonce_State::update(const InitializationVector& iv)
- {
- m_nonce = unlock(iv.bits_of());
- m_fresh_nonce = true;
- }
-
-std::vector<byte> AEAD_Filter::Nonce_State::get()
- {
- BOTAN_ASSERT(m_fresh_nonce, "The nonce is fresh for this message");
-
- m_fresh_nonce = false;
- return m_nonce;
- }
-
-void AEAD_Filter::set_associated_data(const byte ad[], size_t ad_len)
- {
- m_aead->set_associated_data(ad, ad_len);
- }
-
-void AEAD_Filter::set_iv(const InitializationVector& iv)
- {
- m_nonce.update(iv);
- }
-
-void AEAD_Filter::set_key(const SymmetricKey& key)
- {
- m_aead->set_key(key);
- }
-
-Key_Length_Specification AEAD_Filter::key_spec() const
- {
- return m_aead->key_spec();
- }
-
-bool AEAD_Filter::valid_iv_length(size_t length) const
- {
- return m_aead->valid_nonce_length(length);
- }
-
-void AEAD_Filter::write(const byte input[], size_t input_length)
- {
- Buffered_Filter::write(input, input_length);
- }
-
-void AEAD_Filter::end_msg()
- {
- Buffered_Filter::end_msg();
- }
-
-void AEAD_Filter::start_msg()
- {
- send(m_aead->start_vec(m_nonce.get()));
- }
-
-void AEAD_Filter::buffered_block(const byte input[], size_t input_length)
- {
- secure_vector<byte> buf;
-
- while(input_length)
- {
- const size_t take = std::min(m_aead->update_granularity(), input_length);
-
- buf.resize(take);
- copy_mem(&buf[0], input, take);
-
- m_aead->update(buf);
-
- send(buf);
-
- input += take;
- input_length -= take;
- }
- }
-
-void AEAD_Filter::buffered_final(const byte input[], size_t input_length)
- {
- secure_vector<byte> buf(input, input + input_length);
- m_aead->finish(buf);
- send(buf);
- }
-
-}
diff --git a/src/filters/aead_filt/aead_filt.h b/src/filters/aead_filt/aead_filt.h
index 590a60655..65f73d7d3 100644
--- a/src/filters/aead_filt/aead_filt.h
+++ b/src/filters/aead_filt/aead_filt.h
@@ -1,5 +1,5 @@
/*
-* Filter interface for AEAD modes
+* Filter interface for AEAD Modes
* (C) 2013 Jack Lloyd
*
* Distributed under the terms of the Botan license
@@ -8,21 +8,18 @@
#ifndef BOTAN_AEAD_FILTER_H__
#define BOTAN_AEAD_FILTER_H__
-#include <botan/key_filt.h>
-#include <botan/buf_filt.h>
+#include <botan/transform_filter.h>
#include <botan/aead.h>
-#include <memory>
namespace Botan {
/**
-* Filter interface for AEAD modes like EAX and GCM
+* Filter interface for AEAD Modes
*/
-class BOTAN_DLL AEAD_Filter : public Keyed_Filter,
- private Buffered_Filter
+class BOTAN_DLL AEAD_Filter : public Transformation_Filter
{
public:
- AEAD_Filter(AEAD_Mode* aead);
+ AEAD_Filter(AEAD_Mode* aead) : Transformation_Filter(aead) {}
/**
* Set associated data that is not included in the ciphertext but
@@ -32,39 +29,10 @@ class BOTAN_DLL AEAD_Filter : public Keyed_Filter,
* @param ad the associated data
* @param ad_len length of add in bytes
*/
- void set_associated_data(const byte ad[], size_t ad_len);
-
- void set_iv(const InitializationVector& iv) override;
-
- void set_key(const SymmetricKey& key) override;
-
- Key_Length_Specification key_spec() const override;
-
- bool valid_iv_length(size_t length) const override;
-
- std::string name() const override;
-
- private:
- void write(const byte input[], size_t input_length) override;
- void start_msg() override;
- void end_msg() override;
-
- void buffered_block(const byte input[], size_t input_length) override;
- void buffered_final(const byte input[], size_t input_length) override;
-
- class Nonce_State
+ void set_associated_data(const byte ad[], size_t ad_len)
{
- public:
- void update(const InitializationVector& iv);
- std::vector<byte> get();
- private:
- bool m_fresh_nonce = false;
- std::vector<byte> m_nonce;
- };
-
- Nonce_State m_nonce;
- std::unique_ptr<AEAD_Mode> m_aead;
-
+ dynamic_cast<AEAD_Mode&>(get_transform()).set_associated_data(ad, ad_len);
+ }
};
}
diff --git a/src/filters/filters.h b/src/filters/filters.h
index 8fcc2d85d..5973cbf50 100644
--- a/src/filters/filters.h
+++ b/src/filters/filters.h
@@ -28,7 +28,7 @@
namespace Botan {
/**
-* Stream Cipher Filter.
+* Stream Cipher Filter
*/
class BOTAN_DLL StreamCipher_Filter : public Keyed_Filter
{
diff --git a/src/filters/info.txt b/src/filters/info.txt
index b5c03aa45..0c56d8269 100644
--- a/src/filters/info.txt
+++ b/src/filters/info.txt
@@ -13,6 +13,7 @@ pipe_io.cpp
pipe_rw.cpp
secqueue.cpp
threaded_fork.cpp
+transform_filter.cpp
</source>
<header:public>
@@ -25,6 +26,7 @@ filters.h
key_filt.h
pipe.h
secqueue.h
+transform_filter.h
</header:public>
<header:internal>
diff --git a/src/filters/modes/xts/xts.cpp b/src/filters/modes/xts/xts.cpp
deleted file mode 100644
index df38614bb..000000000
--- a/src/filters/modes/xts/xts.cpp
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
-* XTS Mode
-* (C) 2009 Jack Lloyd
-*
-* Distributed under the terms of the Botan license
-*/
-
-#include <botan/xts.h>
-#include <botan/internal/xor_buf.h>
-#include <algorithm>
-#include <stdexcept>
-
-namespace Botan {
-
-namespace {
-
-void poly_double(byte tweak[], size_t size)
- {
- const byte polynomial = (size == 16) ? 0x87 : 0x1B;
-
- byte carry = 0;
- for(size_t i = 0; i != size; ++i)
- {
- byte carry2 = (tweak[i] >> 7);
- tweak[i] = (tweak[i] << 1) | carry;
- carry = carry2;
- }
-
- if(carry)
- tweak[0] ^= polynomial;
- }
-
-/* XTS needs to process at least 2 blocks in parallel
- because block_size+1 bytes are needed at the end
-*/
-size_t xts_parallelism(BlockCipher* cipher)
- {
- return std::max<size_t>(cipher->parallel_bytes(),
- 2 * cipher->block_size());
- }
-
-Key_Length_Specification xts_key_spec(const BlockCipher& cipher)
- {
- const Key_Length_Specification& spec = cipher.key_spec();
-
- return Key_Length_Specification(2*spec.minimum_keylength(),
- 2*spec.maximum_keylength(),
- 2*spec.keylength_multiple());
- }
-
-}
-
-/*
-* XTS_Encryption constructor
-*/
-XTS_Encryption::XTS_Encryption(BlockCipher* ciph) :
- Buffered_Filter(xts_parallelism(ciph), ciph->block_size() + 1),
- cipher(ciph)
- {
- if(cipher->block_size() != 8 && cipher->block_size() != 16)
- throw std::invalid_argument("Bad cipher for XTS: " + cipher->name());
-
- cipher2 = cipher->clone();
- tweak.resize(buffered_block_size());
- }
-
-/*
-* XTS_Encryption constructor
-*/
-XTS_Encryption::XTS_Encryption(BlockCipher* ciph,
- const SymmetricKey& key,
- const InitializationVector& iv) :
- Buffered_Filter(xts_parallelism(ciph), ciph->block_size() + 1),
- cipher(ciph)
- {
- if(cipher->block_size() != 8 && cipher->block_size() != 16)
- throw std::invalid_argument("Bad cipher for XTS: " + cipher->name());
-
- cipher2 = cipher->clone();
- tweak.resize(buffered_block_size());
-
- set_key(key);
- set_iv(iv);
- }
-
-/*
-* Return the name
-*/
-std::string XTS_Encryption::name() const
- {
- return (cipher->name() + "/XTS");
- }
-
-Key_Length_Specification XTS_Encryption::key_spec() const
- {
- return xts_key_spec(*cipher);
- }
-
-/*
-* Set new tweak
-*/
-void XTS_Encryption::set_iv(const InitializationVector& iv)
- {
- if(!valid_iv_length(iv.length()))
- throw Invalid_IV_Length(name(), iv.length());
-
- const size_t blocks_in_tweak = tweak.size() / cipher->block_size();
-
- tweak.assign(iv.begin(), iv.end());
- cipher2->encrypt(tweak);
-
- for(size_t i = 1; i < blocks_in_tweak; ++i)
- {
- buffer_insert(tweak, i*cipher->block_size(),
- &tweak[(i-1)*cipher->block_size()],
- cipher->block_size());
-
- poly_double(&tweak[i*cipher->block_size()], cipher->block_size());
- }
- }
-
-void XTS_Encryption::set_key(const SymmetricKey& key)
- {
- size_t key_half = key.length() / 2;
-
- if(key.length() % 2 == 1 || !cipher->valid_keylength(key_half))
- throw Invalid_Key_Length(name(), key.length());
-
- cipher->set_key(key.begin(), key_half);
- cipher2->set_key(key.begin() + key_half, key_half);
- }
-
-/*
-* Encrypt in XTS mode
-*/
-void XTS_Encryption::write(const byte input[], size_t length)
- {
- Buffered_Filter::write(input, length);
- }
-/*
-* Finish encrypting in XTS mode
-*/
-void XTS_Encryption::end_msg()
- {
- Buffered_Filter::end_msg();
- }
-
-void XTS_Encryption::buffered_block(const byte input[], size_t length)
- {
- const size_t blocks_in_tweak = tweak.size() / cipher->block_size();
- size_t blocks = length / cipher->block_size();
-
- secure_vector<byte> temp(tweak.size());
-
- while(blocks)
- {
- size_t to_proc = std::min(blocks, blocks_in_tweak);
- size_t to_proc_bytes = to_proc * cipher->block_size();
-
- xor_buf(temp, input, tweak, to_proc_bytes);
-
- cipher->encrypt_n(&temp[0], &temp[0], to_proc);
-
- xor_buf(temp, tweak, to_proc_bytes);
-
- send(temp, to_proc_bytes);
-
- copy_mem(&tweak[0],
- &tweak[(to_proc-1)*cipher->block_size()],
- cipher->block_size());
-
- poly_double(&tweak[0], cipher->block_size());
-
- for(size_t i = 1; i < blocks_in_tweak; ++i)
- {
- buffer_insert(tweak, i*cipher->block_size(),
- &tweak[(i-1)*cipher->block_size()],
- cipher->block_size());
-
- poly_double(&tweak[i*cipher->block_size()], cipher->block_size());
- }
-
- input += to_proc * cipher->block_size();
- blocks -= to_proc;
- }
- }
-
-/*
-* Finish encrypting in XTS mode
-*/
-void XTS_Encryption::buffered_final(const byte input[], size_t length)
- {
- if(length <= cipher->block_size())
- throw Encoding_Error("XTS_Encryption: insufficient data to encrypt");
-
- if(length % cipher->block_size() == 0)
- {
- buffered_block(input, length);
- }
- else
- { // steal ciphertext
-
- size_t leftover_blocks =
- ((length / cipher->block_size()) - 1) * cipher->block_size();
-
- buffered_block(input, leftover_blocks);
-
- input += leftover_blocks;
- length -= leftover_blocks;
-
- secure_vector<byte> temp(input, input + length);
-
- xor_buf(temp, tweak, cipher->block_size());
- cipher->encrypt(temp);
- xor_buf(temp, tweak, cipher->block_size());
-
- poly_double(&tweak[0], cipher->block_size());
-
- for(size_t i = 0; i != length - cipher->block_size(); ++i)
- std::swap(temp[i], temp[i + cipher->block_size()]);
-
- xor_buf(temp, tweak, cipher->block_size());
- cipher->encrypt(temp);
- xor_buf(temp, tweak, cipher->block_size());
-
- send(temp, temp.size());
- }
-
- buffer_reset();
- }
-
-/*
-* XTS_Decryption constructor
-*/
-XTS_Decryption::XTS_Decryption(BlockCipher* ciph) :
- Buffered_Filter(xts_parallelism(ciph), ciph->block_size() + 1),
- cipher(ciph)
- {
- if(cipher->block_size() != 8 && cipher->block_size() != 16)
- throw std::invalid_argument("Bad cipher for XTS: " + cipher->name());
-
- cipher2 = ciph->clone();
- tweak.resize(buffered_block_size());
- }
-
-/*
-* XTS_Decryption constructor
-*/
-XTS_Decryption::XTS_Decryption(BlockCipher* ciph,
- const SymmetricKey& key,
- const InitializationVector& iv) :
- Buffered_Filter(xts_parallelism(ciph), ciph->block_size() + 1),
- cipher(ciph)
- {
- if(cipher->block_size() != 8 && cipher->block_size() != 16)
- throw std::invalid_argument("Bad cipher for XTS: " + cipher->name());
-
- cipher2 = ciph->clone();
- tweak.resize(buffered_block_size());
-
- set_key(key);
- set_iv(iv);
- }
-
-/*
-* Return the name
-*/
-std::string XTS_Decryption::name() const
- {
- return (cipher->name() + "/XTS");
- }
-
-Key_Length_Specification XTS_Decryption::key_spec() const
- {
- return xts_key_spec(*cipher);
- }
-
-/*
-* Set new tweak
-*/
-void XTS_Decryption::set_iv(const InitializationVector& iv)
- {
- if(!valid_iv_length(iv.length()))
- throw Invalid_IV_Length(name(), iv.length());
-
- const size_t blocks_in_tweak = tweak.size() / cipher->block_size();
-
- tweak.assign(iv.begin(), iv.end());
- cipher2->encrypt(tweak);
-
- for(size_t i = 1; i < blocks_in_tweak; ++i)
- {
- buffer_insert(tweak, i*cipher->block_size(),
- &tweak[(i-1)*cipher->block_size()],
- cipher->block_size());
-
- poly_double(&tweak[i*cipher->block_size()], cipher->block_size());
- }
- }
-
-void XTS_Decryption::set_key(const SymmetricKey& key)
- {
- size_t key_half = key.length() / 2;
-
- if(key.length() % 2 == 1 || !cipher->valid_keylength(key_half))
- throw Invalid_Key_Length(name(), key.length());
-
- cipher->set_key(key.begin(), key_half);
- cipher2->set_key(key.begin() + key_half, key_half);
- }
-
-/*
-* Decrypt in XTS mode
-*/
-void XTS_Decryption::write(const byte input[], size_t length)
- {
- Buffered_Filter::write(input, length);
- }
-
-/*
-* Finish decrypting in XTS mode
-*/
-void XTS_Decryption::end_msg()
- {
- Buffered_Filter::end_msg();
- }
-
-void XTS_Decryption::buffered_block(const byte input[], size_t input_length)
- {
- const size_t blocks_in_tweak = tweak.size() / cipher->block_size();
- size_t blocks = input_length / cipher->block_size();
-
- secure_vector<byte> temp(tweak.size());
-
- while(blocks)
- {
- size_t to_proc = std::min(blocks, blocks_in_tweak);
- size_t to_proc_bytes = to_proc * cipher->block_size();
-
- xor_buf(temp, input, tweak, to_proc_bytes);
-
- cipher->decrypt_n(&temp[0], &temp[0], to_proc);
-
- xor_buf(temp, tweak, to_proc_bytes);
-
- send(temp, to_proc_bytes);
-
- copy_mem(&tweak[0],
- &tweak[(to_proc-1)*cipher->block_size()],
- cipher->block_size());
-
- poly_double(&tweak[0], cipher->block_size());
-
- for(size_t i = 1; i < blocks_in_tweak; ++i)
- {
- buffer_insert(tweak, i*cipher->block_size(),
- &tweak[(i-1)*cipher->block_size()],
- cipher->block_size());
-
- poly_double(&tweak[i*cipher->block_size()], cipher->block_size());
- }
-
- input += to_proc * cipher->block_size();
- blocks -= to_proc;
- }
- }
-
-void XTS_Decryption::buffered_final(const byte input[], size_t length)
- {
- if(length <= cipher->block_size())
- throw Decoding_Error("XTS_Decryption: insufficient data to decrypt");
-
- if(length % cipher->block_size() == 0)
- {
- buffered_block(input, length);
- }
- else
- {
- size_t leftover_blocks =
- ((length / cipher->block_size()) - 1) * cipher->block_size();
-
- buffered_block(input, leftover_blocks);
-
- input += leftover_blocks;
- length -= leftover_blocks;
-
- secure_vector<byte> temp(input, input + length);
- secure_vector<byte> tweak_copy(&tweak[0], &tweak[cipher->block_size()]);
-
- poly_double(&tweak_copy[0], cipher->block_size());
-
- xor_buf(temp, tweak_copy, cipher->block_size());
- cipher->decrypt(temp);
- xor_buf(temp, tweak_copy, cipher->block_size());
-
- for(size_t i = 0; i != length - cipher->block_size(); ++i)
- std::swap(temp[i], temp[i + cipher->block_size()]);
-
- xor_buf(temp, tweak, cipher->block_size());
- cipher->decrypt(temp);
- xor_buf(temp, tweak, cipher->block_size());
-
- send(temp, length);
- }
-
- buffer_reset();
- }
-
-}
diff --git a/src/filters/modes/xts/xts.h b/src/filters/modes/xts/xts.h
deleted file mode 100644
index 05a779703..000000000
--- a/src/filters/modes/xts/xts.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
-* XTS mode, from IEEE P1619
-* (C) 2009 Jack Lloyd
-*
-* Distributed under the terms of the Botan license
-*/
-
-#ifndef BOTAN_XTS_H__
-#define BOTAN_XTS_H__
-
-#include <botan/block_cipher.h>
-#include <botan/key_filt.h>
-#include <botan/buf_filt.h>
-
-namespace Botan {
-
-/**
-* IEEE P1619 XTS Encryption
-*/
-class BOTAN_DLL XTS_Encryption : public Keyed_Filter,
- private Buffered_Filter
- {
- public:
- void set_key(const SymmetricKey& key);
- void set_iv(const InitializationVector& iv);
-
- Key_Length_Specification key_spec() const override;
-
- bool valid_iv_length(size_t iv_len) const
- { return (iv_len == cipher->block_size()); }
-
- std::string name() const;
-
- XTS_Encryption(BlockCipher* ciph);
-
- XTS_Encryption(BlockCipher* ciph,
- const SymmetricKey& key,
- const InitializationVector& iv);
-
- ~XTS_Encryption() { delete cipher; delete cipher2; }
- private:
- void write(const byte[], size_t);
- void end_msg();
-
- void buffered_block(const byte input[], size_t input_length);
- void buffered_final(const byte input[], size_t input_length);
-
- BlockCipher* cipher;
- BlockCipher* cipher2;
- secure_vector<byte> tweak;
- };
-
-/**
-* IEEE P1619 XTS Encryption
-*/
-class BOTAN_DLL XTS_Decryption : public Keyed_Filter,
- private Buffered_Filter
- {
- public:
- void set_key(const SymmetricKey& key);
- void set_iv(const InitializationVector& iv);
-
- Key_Length_Specification key_spec() const override;
-
- bool valid_iv_length(size_t iv_len) const
- { return (iv_len == cipher->block_size()); }
-
- std::string name() const;
-
- XTS_Decryption(BlockCipher* ciph);
-
- XTS_Decryption(BlockCipher* ciph,
- const SymmetricKey& key,
- const InitializationVector& iv);
-
- ~XTS_Decryption() { delete cipher; delete cipher2; }
- private:
- void write(const byte[], size_t);
- void end_msg();
-
- void buffered_block(const byte input[], size_t input_length);
- void buffered_final(const byte input[], size_t input_length);
-
- BlockCipher* cipher;
- BlockCipher* cipher2;
- secure_vector<byte> tweak;
- };
-
-}
-
-#endif
diff --git a/src/filters/transform_filter.cpp b/src/filters/transform_filter.cpp
new file mode 100644
index 000000000..e12328af8
--- /dev/null
+++ b/src/filters/transform_filter.cpp
@@ -0,0 +1,99 @@
+/*
+* Filter interface for Transformations
+* (C) 2013 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/transform_filter.h>
+
+namespace Botan {
+
+Transformation_Filter::Transformation_Filter(Transformation* transform) :
+ Buffered_Filter(transform->update_granularity(),
+ transform->minimum_final_size()),
+ m_transform(transform)
+ {
+ }
+
+std::string Transformation_Filter::name() const
+ {
+ return m_transform->name();
+ }
+
+void Transformation_Filter::Nonce_State::update(const InitializationVector& iv)
+ {
+ m_nonce = unlock(iv.bits_of());
+ m_fresh_nonce = true;
+ }
+
+std::vector<byte> Transformation_Filter::Nonce_State::get()
+ {
+ BOTAN_ASSERT(m_fresh_nonce, "The nonce is fresh for this message");
+
+ if(!m_nonce.empty())
+ m_fresh_nonce = false;
+ return m_nonce;
+ }
+
+void Transformation_Filter::set_iv(const InitializationVector& iv)
+ {
+ m_nonce.update(iv);
+ }
+
+void Transformation_Filter::set_key(const SymmetricKey& key)
+ {
+ m_transform->set_key(key);
+ }
+
+Key_Length_Specification Transformation_Filter::key_spec() const
+ {
+ return m_transform->key_spec();
+ }
+
+bool Transformation_Filter::valid_iv_length(size_t length) const
+ {
+ return m_transform->valid_nonce_length(length);
+ }
+
+void Transformation_Filter::write(const byte input[], size_t input_length)
+ {
+ Buffered_Filter::write(input, input_length);
+ }
+
+void Transformation_Filter::end_msg()
+ {
+ Buffered_Filter::end_msg();
+ }
+
+void Transformation_Filter::start_msg()
+ {
+ send(m_transform->start_vec(m_nonce.get()));
+ }
+
+void Transformation_Filter::buffered_block(const byte input[], size_t input_length)
+ {
+ secure_vector<byte> buf;
+
+ while(input_length)
+ {
+ const size_t take = std::min(m_transform->update_granularity(), input_length);
+
+ buf.assign(input, input + take);
+ m_transform->update(buf);
+
+ send(buf);
+
+ input += take;
+ input_length -= take;
+ }
+ }
+
+void Transformation_Filter::buffered_final(const byte input[], size_t input_length)
+ {
+ secure_vector<byte> buf(input, input + input_length);
+ m_transform->finish(buf);
+ send(buf);
+ }
+
+}
diff --git a/src/filters/transform_filter.h b/src/filters/transform_filter.h
new file mode 100644
index 000000000..5ad66ba96
--- /dev/null
+++ b/src/filters/transform_filter.h
@@ -0,0 +1,67 @@
+/*
+* Filter interface for Transformations
+* (C) 2013 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TRANSFORMATION_FILTER_H__
+#define BOTAN_TRANSFORMATION_FILTER_H__
+
+#include <botan/transform.h>
+#include <botan/key_filt.h>
+#include <botan/buf_filt.h>
+#include <memory>
+
+namespace Botan {
+
+/**
+* Filter interface for Transformations
+*/
+class BOTAN_DLL Transformation_Filter : public Keyed_Filter,
+ private Buffered_Filter
+ {
+ public:
+ Transformation_Filter(Transformation* t);
+
+ void set_iv(const InitializationVector& iv) override;
+
+ void set_key(const SymmetricKey& key) override;
+
+ Key_Length_Specification key_spec() const override;
+
+ bool valid_iv_length(size_t length) const override;
+
+ std::string name() const override;
+
+ protected:
+ const Transformation& get_transform() const { return *m_transform; }
+
+ Transformation& get_transform() { return *m_transform; }
+
+ private:
+ void write(const byte input[], size_t input_length) override;
+ void start_msg() override;
+ void end_msg() override;
+
+ void buffered_block(const byte input[], size_t input_length) override;
+ void buffered_final(const byte input[], size_t input_length) override;
+
+ class Nonce_State
+ {
+ public:
+ void update(const InitializationVector& iv);
+ std::vector<byte> get();
+ private:
+ bool m_fresh_nonce = false;
+ std::vector<byte> m_nonce;
+ };
+
+ Nonce_State m_nonce;
+ std::unique_ptr<Transformation> m_transform;
+
+ };
+
+}
+
+#endif
diff --git a/src/aead/aead.cpp b/src/modes/aead/aead.cpp
index 1ec7b4a4a..1ec7b4a4a 100644
--- a/src/aead/aead.cpp
+++ b/src/modes/aead/aead.cpp
diff --git a/src/modes/aead/aead.h b/src/modes/aead/aead.h
new file mode 100644
index 000000000..97f156d60
--- /dev/null
+++ b/src/modes/aead/aead.h
@@ -0,0 +1,59 @@
+/*
+* Interface for AEAD modes
+* (C) 2013 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_AEAD_MODE_H__
+#define BOTAN_AEAD_MODE_H__
+
+#include <botan/transform.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 AEAD_Mode : public Transformation
+ {
+ public:
+ /**
+ * Set associated data that is not included in the ciphertext but
+ * that should be authenticated. Must be called after set_key
+ * and before finish.
+ *
+ * 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 byte ad[], size_t ad_len) = 0;
+
+ template<typename Alloc>
+ void set_associated_data_vec(const std::vector<byte, Alloc>& ad)
+ {
+ set_associated_data(&ad[0], ad.size());
+ }
+
+ /**
+ * Default AEAD nonce size (a commonly supported value among AEAD
+ * modes, and, large enough that random collisions are unlikely).
+ */
+ size_t default_nonce_size() const override { return 12; }
+ };
+
+/**
+* Get an AEAD mode by name (eg "AES-128/GCM" or "Serpent/EAX")
+*/
+BOTAN_DLL AEAD_Mode* get_aead(const std::string& name, Cipher_Dir direction);
+
+}
+
+#endif
diff --git a/src/aead/eax/eax.cpp b/src/modes/aead/eax/eax.cpp
index c6aaa9e85..c6aaa9e85 100644
--- a/src/aead/eax/eax.cpp
+++ b/src/modes/aead/eax/eax.cpp
diff --git a/src/aead/eax/eax.h b/src/modes/aead/eax/eax.h
index 6815e3ce0..6815e3ce0 100644
--- a/src/aead/eax/eax.h
+++ b/src/modes/aead/eax/eax.h
diff --git a/src/aead/eax/info.txt b/src/modes/aead/eax/info.txt
index 94924e682..94924e682 100644
--- a/src/aead/eax/info.txt
+++ b/src/modes/aead/eax/info.txt
diff --git a/src/aead/gcm/gcm.cpp b/src/modes/aead/gcm/gcm.cpp
index 7b8e0aa36..7b8e0aa36 100644
--- a/src/aead/gcm/gcm.cpp
+++ b/src/modes/aead/gcm/gcm.cpp
diff --git a/src/aead/gcm/gcm.h b/src/modes/aead/gcm/gcm.h
index e1479c27f..e1479c27f 100644
--- a/src/aead/gcm/gcm.h
+++ b/src/modes/aead/gcm/gcm.h
diff --git a/src/aead/gcm/info.txt b/src/modes/aead/gcm/info.txt
index 698cd0803..698cd0803 100644
--- a/src/aead/gcm/info.txt
+++ b/src/modes/aead/gcm/info.txt
diff --git a/src/aead/info.txt b/src/modes/aead/info.txt
index c2985ea85..c2985ea85 100644
--- a/src/aead/info.txt
+++ b/src/modes/aead/info.txt
diff --git a/src/aead/ocb/info.txt b/src/modes/aead/ocb/info.txt
index 8d6a93ed9..8d6a93ed9 100644
--- a/src/aead/ocb/info.txt
+++ b/src/modes/aead/ocb/info.txt
diff --git a/src/aead/ocb/ocb.cpp b/src/modes/aead/ocb/ocb.cpp
index 4cbd8bde8..4cbd8bde8 100644
--- a/src/aead/ocb/ocb.cpp
+++ b/src/modes/aead/ocb/ocb.cpp
diff --git a/src/aead/ocb/ocb.h b/src/modes/aead/ocb/ocb.h
index ea7729348..ea7729348 100644
--- a/src/aead/ocb/ocb.h
+++ b/src/modes/aead/ocb/ocb.h
diff --git a/src/filters/modes/xts/info.txt b/src/modes/info.txt
index 7327298f9..0dcb0cd59 100644
--- a/src/filters/modes/xts/info.txt
+++ b/src/modes/info.txt
@@ -1,4 +1,3 @@
-define XTS
<requires>
block
diff --git a/src/modes/xts/info.txt b/src/modes/xts/info.txt
new file mode 100644
index 000000000..29b58afe4
--- /dev/null
+++ b/src/modes/xts/info.txt
@@ -0,0 +1 @@
+define MODE_XTS
diff --git a/src/modes/xts/xts.cpp b/src/modes/xts/xts.cpp
new file mode 100644
index 000000000..5e10fa8ce
--- /dev/null
+++ b/src/modes/xts/xts.cpp
@@ -0,0 +1,292 @@
+/*
+* XTS Mode
+* (C) 2009,2013 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/xts.h>
+#include <botan/loadstor.h>
+#include <botan/internal/xor_buf.h>
+#include <botan/internal/rounding.h>
+
+namespace Botan {
+
+namespace {
+
+void poly_double_128(byte out[], const byte in[])
+ {
+ u64bit X0 = load_le<u64bit>(in, 0);
+ u64bit X1 = load_le<u64bit>(in, 1);
+
+ const bool carry = (X1 >> 63);
+
+ X1 = (X1 << 1) | (X0 >> 63);
+ X0 = (X0 << 1);
+
+ if(carry)
+ X0 ^= 0x87;
+
+ store_le(out, X0, X1);
+ }
+
+void poly_double_64(byte out[], const byte in[])
+ {
+ u64bit X = load_le<u64bit>(in, 0);
+ const bool carry = (X >> 63);
+ X <<= 1;
+ if(carry)
+ X ^= 0x1B;
+ store_le(X, out);
+ }
+
+inline void poly_double(byte out[], const byte in[], size_t size)
+ {
+ if(size == 8)
+ poly_double_64(out, in);
+ else
+ poly_double_128(out, in);
+ }
+
+}
+
+XTS_Mode::XTS_Mode(BlockCipher* cipher) : m_cipher(cipher)
+ {
+ if(m_cipher->block_size() != 8 && m_cipher->block_size() != 16)
+ throw std::invalid_argument("Bad cipher for XTS: " + cipher->name());
+
+ m_tweak_cipher.reset(m_cipher->clone());
+ m_tweak.resize(update_granularity());
+ }
+
+void XTS_Mode::clear()
+ {
+ m_cipher->clear();
+ m_tweak_cipher->clear();
+ zeroise(m_tweak);
+ }
+
+std::string XTS_Mode::name() const
+ {
+ return cipher().name() + "/XTS";
+ }
+
+size_t XTS_Mode::update_granularity() const
+ {
+ return cipher().parallel_bytes();
+ }
+
+size_t XTS_Mode::minimum_final_size() const
+ {
+ return cipher().block_size() + 1;
+ }
+
+Key_Length_Specification XTS_Mode::key_spec() const
+ {
+ const Key_Length_Specification spec = cipher().key_spec();
+
+ return Key_Length_Specification(2*spec.minimum_keylength(),
+ 2*spec.maximum_keylength(),
+ 2*spec.keylength_multiple());
+ }
+
+size_t XTS_Mode::default_nonce_size() 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 byte 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[0], key_half);
+ m_tweak_cipher->set_key(&key[key_half], key_half);
+ }
+
+secure_vector<byte> XTS_Mode::start(const byte nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ const size_t BS = m_tweak_cipher->block_size();
+ const size_t blocks_in_tweak = update_granularity() / BS;
+
+ copy_mem(&m_tweak[0], nonce, nonce_len);
+ m_tweak_cipher->encrypt(&m_tweak[0]);
+
+ update_tweak(0);
+
+ return secure_vector<byte>();
+ }
+
+void XTS_Mode::update_tweak(size_t which)
+ {
+ const size_t BS = m_tweak_cipher->block_size();
+
+ if(which > 0)
+ poly_double(&m_tweak[0], &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(&m_tweak[i*BS], &m_tweak[(i-1)*BS], BS);
+ }
+
+size_t XTS_Encryption::output_length(size_t input_length) const
+ {
+ return round_up(input_length, cipher().block_size());
+ }
+
+void XTS_Encryption::update(secure_vector<byte>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ byte* buf = &buffer[offset];
+
+ 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);
+ const size_t to_proc_bytes = to_proc * BS;
+
+ xor_buf(buf, tweak(), to_proc_bytes);
+ cipher().encrypt_n(buf, buf, to_proc);
+ xor_buf(buf, tweak(), to_proc_bytes);
+
+ buf += to_proc * BS;
+ blocks -= to_proc;
+
+ update_tweak(to_proc);
+ }
+ }
+
+void XTS_Encryption::finish(secure_vector<byte>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ byte* buf = &buffer[offset];
+
+ BOTAN_ASSERT(sz >= minimum_final_size(), "Have sufficient final input");
+
+ 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<byte> 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)
+ std::swap(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
+ {
+ // might be less
+ return input_length;
+ }
+
+void XTS_Decryption::update(secure_vector<byte>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ byte* buf = &buffer[offset];
+
+ 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);
+ const size_t to_proc_bytes = to_proc * BS;
+
+ xor_buf(buf, tweak(), to_proc_bytes);
+ cipher().decrypt_n(buf, buf, to_proc);
+ xor_buf(buf, tweak(), to_proc_bytes);
+
+ buf += to_proc * BS;
+ blocks -= to_proc;
+
+ update_tweak(to_proc);
+ }
+ }
+
+void XTS_Decryption::finish(secure_vector<byte>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ byte* buf = &buffer[offset];
+
+ BOTAN_ASSERT(sz >= minimum_final_size(), "Have sufficient final input");
+
+ 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<byte> 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)
+ std::swap(last[i], last[i + BS]);
+
+ xor_buf(last, tweak(), BS);
+ cipher().decrypt(last);
+ xor_buf(last, tweak(), BS);
+
+ buffer += last;
+ }
+ }
+
+}
diff --git a/src/modes/xts/xts.h b/src/modes/xts/xts.h
new file mode 100644
index 000000000..2163bb447
--- /dev/null
+++ b/src/modes/xts/xts.h
@@ -0,0 +1,86 @@
+/*
+* XTS mode, from IEEE P1619
+* (C) 2009,2013 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_MODE_XTS_H__
+#define BOTAN_MODE_XTS_H__
+
+#include <botan/transform.h>
+#include <botan/block_cipher.h>
+#include <memory>
+
+namespace Botan {
+
+/**
+* IEEE P1619 XTS Mode
+*/
+class XTS_Mode : public Transformation
+ {
+ public:
+ std::string name() const override;
+
+ secure_vector<byte> start(const byte nonce[], size_t nonce_len) override;
+
+ size_t update_granularity() const;
+
+ size_t minimum_final_size() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ size_t default_nonce_size() const override;
+
+ bool valid_nonce_length(size_t n) const override;
+
+ void clear();
+ protected:
+ XTS_Mode(BlockCipher* cipher);
+
+ const byte* tweak() const { return &m_tweak[0]; }
+
+ const BlockCipher& cipher() const { return *m_cipher; }
+
+ void update_tweak(size_t last_used);
+
+ private:
+ void key_schedule(const byte key[], size_t length) override;
+
+ std::unique_ptr<BlockCipher> m_cipher, m_tweak_cipher;
+ secure_vector<byte> m_tweak;
+ };
+
+/**
+* IEEE P1619 XTS Encryption
+*/
+class BOTAN_DLL XTS_Encryption : public XTS_Mode
+ {
+ public:
+ XTS_Encryption(BlockCipher* cipher) : XTS_Mode(cipher) {}
+
+ void update(secure_vector<byte>& blocks, size_t offset) override;
+
+ void finish(secure_vector<byte>& final_block, size_t offset) override;
+
+ size_t output_length(size_t input_length) const override;
+ };
+
+/**
+* IEEE P1619 XTS Decryption
+*/
+class BOTAN_DLL XTS_Decryption : public XTS_Mode
+ {
+ public:
+ XTS_Decryption(BlockCipher* cipher) : XTS_Mode(cipher) {}
+
+ void update(secure_vector<byte>& blocks, size_t offset) override;
+
+ void finish(secure_vector<byte>& final_block, size_t offset) override;
+
+ size_t output_length(size_t input_length) const override;
+ };
+
+}
+
+#endif