aboutsummaryrefslogtreecommitdiffstats
path: root/src/modes/cbc
diff options
context:
space:
mode:
authorlloyd <[email protected]>2013-08-15 20:46:15 +0000
committerlloyd <[email protected]>2013-08-15 20:46:15 +0000
commit7328d76b78c3c923c11cc01aa5cf8e5498ea02ff (patch)
treeb62a69987d18d0b8d010329f11f4a72cf0b3ecce /src/modes/cbc
parent10bab015381aceecdf37bc7c7c325e014f2da676 (diff)
Convert CTS mode to Transformation API
Diffstat (limited to 'src/modes/cbc')
-rw-r--r--src/modes/cbc/cbc.cpp129
-rw-r--r--src/modes/cbc/cbc.h38
2 files changed, 160 insertions, 7 deletions
diff --git a/src/modes/cbc/cbc.cpp b/src/modes/cbc/cbc.cpp
index bb7c56858..96e5be6e2 100644
--- a/src/modes/cbc/cbc.cpp
+++ b/src/modes/cbc/cbc.cpp
@@ -10,6 +10,9 @@
#include <botan/internal/xor_buf.h>
#include <botan/internal/rounding.h>
+#include <iostream>
+#include <botan/hex.h>
+
namespace Botan {
CBC_Mode::CBC_Mode(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) :
@@ -17,7 +20,7 @@ CBC_Mode::CBC_Mode(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) :
m_padding(padding),
m_state(m_cipher->block_size())
{
- if(!m_padding->valid_blocksize(cipher->block_size()))
+ if(m_padding && !m_padding->valid_blocksize(cipher->block_size()))
throw std::invalid_argument("Padding " + m_padding->name() +
" cannot be used with " +
cipher->name() + "/CBC");
@@ -31,7 +34,10 @@ void CBC_Mode::clear()
std::string CBC_Mode::name() const
{
- return cipher().name() + "/CBC/" + padding().name();
+ if(m_padding)
+ return cipher().name() + "/CBC/" + padding().name();
+ else
+ return cipher().name() + "/CBC/CTS";
}
size_t CBC_Mode::update_granularity() const
@@ -98,7 +104,7 @@ void CBC_Encryption::update(secure_vector<byte>& buffer, size_t offset)
if(blocks)
{
- xor_buf(&buf[0], &state()[0], BS);
+ xor_buf(&buf[0], state_ptr(), BS);
cipher().encrypt(&buf[0]);
for(size_t i = 1; i != blocks; ++i)
@@ -115,7 +121,6 @@ void CBC_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];
const size_t BS = cipher().block_size();
@@ -142,9 +147,68 @@ void CBC_Encryption::finish(secure_vector<byte>& buffer, size_t offset)
update(buffer, offset);
}
+bool CTS_Encryption::valid_nonce_length(size_t n) const
+ {
+ return (n == cipher().block_size());
+ }
+
+size_t CTS_Encryption::minimum_final_size() const
+ {
+ return cipher().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<byte>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ byte* buf = &buffer[offset];
+ const size_t sz = buffer.size() - offset;
+
+ const size_t BS = cipher().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<byte> last(buf + full_blocks, buf + full_blocks + final_bytes);
+ buffer.resize(full_blocks + offset);
+ update(buffer, offset);
+
+ xor_buf(&last[0], state_ptr(), BS);
+ cipher().encrypt(&last[0]);
+
+ for(size_t i = 0; i != final_bytes - BS; ++i)
+ {
+ std::swap(last[i], last[i + BS]);
+ last[i] ^= last[i + BS];
+ }
+
+ cipher().encrypt(&last[0]);
+
+ buffer += last;
+ }
+ }
+
size_t CBC_Decryption::output_length(size_t input_length) const
{
- return input_length;
+ return input_length; // precise for CTS, worst case otherwise
}
size_t CBC_Decryption::minimum_final_size() const
@@ -165,7 +229,7 @@ void CBC_Decryption::update(secure_vector<byte>& buffer, size_t offset)
while(blocks)
{
- const size_t to_proc = std::min(sz, m_tempbuf.size());
+ const size_t to_proc = std::min(BS * blocks, m_tempbuf.size());
cipher().decrypt_n(buf, &m_tempbuf[0], to_proc / BS);
@@ -196,4 +260,57 @@ void CBC_Decryption::finish(secure_vector<byte>& buffer, size_t offset)
buffer.resize(buffer.size() - pad_bytes); // remove padding
}
+bool CTS_Decryption::valid_nonce_length(size_t n) const
+ {
+ return (n == cipher().block_size());
+ }
+
+size_t CTS_Decryption::minimum_final_size() const
+ {
+ return cipher().block_size() + 1;
+ }
+
+void CTS_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];
+
+ const size_t BS = cipher().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<byte> last(buf + full_blocks, buf + full_blocks + final_bytes);
+ buffer.resize(full_blocks + offset);
+ update(buffer, offset);
+
+ cipher().decrypt(&last[0]);
+ xor_buf(&last[0], &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[0]);
+ xor_buf(&last[0], state_ptr(), BS);
+
+ buffer += last;
+ }
+
+ }
+
}
diff --git a/src/modes/cbc/cbc.h b/src/modes/cbc/cbc.h
index add6a206f..68a1f7bcd 100644
--- a/src/modes/cbc/cbc.h
+++ b/src/modes/cbc/cbc.h
@@ -39,7 +39,11 @@ class CBC_Mode : public Transformation
const BlockCipher& cipher() const { return *m_cipher; }
- const BlockCipherModePaddingMethod& padding() const { return *m_padding; }
+ const BlockCipherModePaddingMethod& padding() const
+ {
+ BOTAN_ASSERT_NONNULL(m_padding);
+ return *m_padding;
+ }
secure_vector<byte>& state() { return m_state; }
@@ -72,6 +76,23 @@ class BOTAN_DLL CBC_Encryption : public CBC_Mode
};
/**
+* CBC Encryption with ciphertext stealing (CBC-CS3 variant)
+*/
+class BOTAN_DLL CTS_Encryption : public CBC_Encryption
+ {
+ public:
+ CTS_Encryption(BlockCipher* cipher) : CBC_Encryption(cipher, nullptr) {}
+
+ size_t output_length(size_t input_length) const override;
+
+ void finish(secure_vector<byte>& final_block, size_t offset) override;
+
+ size_t minimum_final_size() const override;
+
+ bool valid_nonce_length(size_t n) const;
+ };
+
+/**
* CBC Decryption
*/
class BOTAN_DLL CBC_Decryption : public CBC_Mode
@@ -91,6 +112,21 @@ class BOTAN_DLL CBC_Decryption : public CBC_Mode
secure_vector<byte> m_tempbuf;
};
+/**
+* CBC Decryption with ciphertext stealing (CBC-CS3 variant)
+*/
+class BOTAN_DLL CTS_Decryption : public CBC_Decryption
+ {
+ public:
+ CTS_Decryption(BlockCipher* cipher) : CBC_Decryption(cipher, nullptr) {}
+
+ void finish(secure_vector<byte>& final_block, size_t offset) override;
+
+ size_t minimum_final_size() const override;
+
+ bool valid_nonce_length(size_t n) const;
+ };
+
}
#endif