From 76018ba3d8c9778c9ef48aece0e84945e8ad4f7e Mon Sep 17 00:00:00 2001 From: lloyd Date: Sun, 27 Dec 2009 00:56:23 +0000 Subject: Implement CBC mode using Buffered_Operation. CBC decryption now runs in parallel, giving major speedups for SIMD-ized algorithms. --- src/filters/modes/cbc/cbc.cpp | 160 +++++++++++++++++++++++++++--------------- src/filters/modes/cbc/cbc.h | 17 +++-- 2 files changed, 114 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/filters/modes/cbc/cbc.cpp b/src/filters/modes/cbc/cbc.cpp index 48ecdf509..9303dd9fd 100644 --- a/src/filters/modes/cbc/cbc.cpp +++ b/src/filters/modes/cbc/cbc.cpp @@ -9,6 +9,8 @@ #include #include +using namespace std::tr1::placeholders; + namespace Botan { /* @@ -16,14 +18,15 @@ namespace Botan { */ CBC_Encryption::CBC_Encryption(BlockCipher* ciph, BlockCipherModePaddingMethod* pad) : - cipher(ciph), padder(pad) + cipher(ciph), padder(pad), + buf_op(std::tr1::bind(&CBC_Encryption::cbc_encrypt, this, _1, _2), + std::tr1::bind(&CBC_Encryption::cbc_final, this, _1, _2), + 2 * cipher->BLOCK_SIZE) { if(!padder->valid_blocksize(cipher->BLOCK_SIZE)) throw Invalid_Block_Size(name(), padder->name()); - buffer.resize(cipher->BLOCK_SIZE); state.resize(cipher->BLOCK_SIZE); - position = 0; } /* @@ -33,14 +36,15 @@ CBC_Encryption::CBC_Encryption(BlockCipher* ciph, BlockCipherModePaddingMethod* pad, const SymmetricKey& key, const InitializationVector& iv) : - cipher(ciph), padder(pad) + cipher(ciph), padder(pad), + buf_op(std::tr1::bind(&CBC_Encryption::cbc_encrypt, this, _1, _2), + std::tr1::bind(&CBC_Encryption::cbc_final, this, _1, _2), + 2 * cipher->BLOCK_SIZE) { if(!padder->valid_blocksize(cipher->BLOCK_SIZE)) throw Invalid_Block_Size(name(), padder->name()); - buffer.resize(cipher->BLOCK_SIZE); state.resize(cipher->BLOCK_SIZE); - position = 0; set_key(key); set_iv(iv); @@ -55,41 +59,51 @@ void CBC_Encryption::set_iv(const InitializationVector& iv) throw Invalid_IV_Length(name(), iv.length()); state = iv.bits_of(); - buffer.clear(); - position = 0; + buf_op.reset(); } /* * Encrypt in CBC mode */ -void CBC_Encryption::write(const byte input[], u32bit length) +void CBC_Encryption::cbc_encrypt(const byte input[], u32bit length) { - while(length) + u32bit blocks = length / state.size(); + + for(u32bit i = 0; i != blocks; ++i) { - u32bit xored = std::min(cipher->BLOCK_SIZE - position, length); - xor_buf(state + position, input, xored); - input += xored; - length -= xored; - position += xored; - if(position == cipher->BLOCK_SIZE) - { - cipher->encrypt(state); - send(state, cipher->BLOCK_SIZE); - position = 0; - } + xor_buf(state, input + i * cipher->BLOCK_SIZE, state.size()); + cipher->encrypt(state); + send(state, state.size()); } } /* * Finish encrypting in CBC mode */ +void CBC_Encryption::cbc_final(const byte input[], u32bit length) + { + if(length % cipher->BLOCK_SIZE == 0) + cbc_encrypt(input, length); + else if(length != 0) + throw Exception(name() + ": Did not pad to full blocksize"); + } + +void CBC_Encryption::write(const byte input[], u32bit input_length) + { + buf_op.write(input, input_length); + } + void CBC_Encryption::end_msg() { + u32bit last_block = buf_op.current_position() % cipher->BLOCK_SIZE; + SecureVector padding(cipher->BLOCK_SIZE); - padder->pad(padding, padding.size(), position); - write(padding, padder->pad_bytes(cipher->BLOCK_SIZE, position)); - if(position != 0) - throw Exception(name() + ": Did not pad to full blocksize"); + padder->pad(padding, padding.size(), last_block); + + u32bit pad_bytes = padder->pad_bytes(cipher->BLOCK_SIZE, last_block); + + buf_op.write(padding, pad_bytes); + buf_op.final(); } /* @@ -105,15 +119,16 @@ std::string CBC_Encryption::name() const */ CBC_Decryption::CBC_Decryption(BlockCipher* ciph, BlockCipherModePaddingMethod* pad) : - cipher(ciph), padder(pad) + cipher(ciph), padder(pad), + buf_op(std::tr1::bind(&CBC_Decryption::cbc_decrypt, this, _1, _2), + std::tr1::bind(&CBC_Decryption::cbc_final, this, _1, _2), + 2 * cipher->BLOCK_SIZE, cipher->BLOCK_SIZE) { if(!padder->valid_blocksize(cipher->BLOCK_SIZE)) throw Invalid_Block_Size(name(), padder->name()); - buffer.resize(cipher->BLOCK_SIZE); state.resize(cipher->BLOCK_SIZE); - temp.resize(cipher->BLOCK_SIZE); - position = 0; + temp.resize(BOTAN_PARALLEL_BLOCKS_CBC * cipher->BLOCK_SIZE); } /* @@ -123,15 +138,16 @@ CBC_Decryption::CBC_Decryption(BlockCipher* ciph, BlockCipherModePaddingMethod* pad, const SymmetricKey& key, const InitializationVector& iv) : - cipher(ciph), padder(pad) + cipher(ciph), padder(pad), + buf_op(std::tr1::bind(&CBC_Decryption::cbc_decrypt, this, _1, _2), + std::tr1::bind(&CBC_Decryption::cbc_final, this, _1, _2), + 2 * cipher->BLOCK_SIZE, cipher->BLOCK_SIZE) { if(!padder->valid_blocksize(cipher->BLOCK_SIZE)) throw Invalid_Block_Size(name(), padder->name()); - buffer.resize(cipher->BLOCK_SIZE); state.resize(cipher->BLOCK_SIZE); - temp.resize(cipher->BLOCK_SIZE); - position = 0; + temp.resize(BOTAN_PARALLEL_BLOCKS_CBC * cipher->BLOCK_SIZE); set_key(key); set_iv(iv); @@ -146,46 +162,74 @@ void CBC_Decryption::set_iv(const InitializationVector& iv) throw Invalid_IV_Length(name(), iv.length()); state = iv.bits_of(); - buffer.clear(); - position = 0; + buf_op.reset(); } /* * Decrypt in CBC mode */ -void CBC_Decryption::write(const byte input[], u32bit length) +void CBC_Decryption::cbc_decrypt(const byte input[], u32bit length) { - while(length) + const u32bit blocks_in_temp = temp.size() / cipher->BLOCK_SIZE; + u32bit blocks = length / cipher->BLOCK_SIZE; + + while(blocks) { - if(position == cipher->BLOCK_SIZE) - { - cipher->decrypt(buffer, temp); - xor_buf(temp, state, cipher->BLOCK_SIZE); - send(temp, cipher->BLOCK_SIZE); - state = buffer; - position = 0; - } - - u32bit added = std::min(cipher->BLOCK_SIZE - position, length); - buffer.copy(position, input, added); - input += added; - length -= added; - position += added; + u32bit to_proc = std::min(blocks, blocks_in_temp); + + cipher->decrypt_n(input, &temp[0], to_proc); + + xor_buf(temp, state, cipher->BLOCK_SIZE); + + for(u32bit i = 1; i < to_proc; ++i) + xor_buf(temp + i * cipher->BLOCK_SIZE, + input + (i-1) * cipher->BLOCK_SIZE, + cipher->BLOCK_SIZE); + + state.set(input + (to_proc - 1) * cipher->BLOCK_SIZE, cipher->BLOCK_SIZE); + + send(temp, to_proc * cipher->BLOCK_SIZE); + + input += to_proc * cipher->BLOCK_SIZE; + blocks -= to_proc; } } /* -* Finish decrypting in CBC mode +* Finish encrypting in CBC mode */ -void CBC_Decryption::end_msg() +void CBC_Decryption::cbc_final(const byte input[], u32bit length) { - if(position != cipher->BLOCK_SIZE) - throw Decoding_Error(name()); - cipher->decrypt(buffer, temp); + if(length == 0 || length % cipher->BLOCK_SIZE != 0) + throw Decoding_Error(name() + ": Ciphertext not multiple of block size"); + + size_t extra_blocks = (length - 1) / cipher->BLOCK_SIZE; + + cbc_decrypt(input, extra_blocks * cipher->BLOCK_SIZE); + + input += extra_blocks * cipher->BLOCK_SIZE; + + cipher->decrypt(input, temp); xor_buf(temp, state, cipher->BLOCK_SIZE); send(temp, padder->unpad(temp, cipher->BLOCK_SIZE)); - state = buffer; - position = 0; + + state.set(input, state.size()); + } + +/* +* Decrypt in CBC mode +*/ +void CBC_Decryption::write(const byte input[], u32bit length) + { + buf_op.write(input, length); + } + +/* +* Finish decrypting in CBC mode +*/ +void CBC_Decryption::end_msg() + { + buf_op.final(); } /* diff --git a/src/filters/modes/cbc/cbc.h b/src/filters/modes/cbc/cbc.h index 91ab21ab6..8fa322260 100644 --- a/src/filters/modes/cbc/cbc.h +++ b/src/filters/modes/cbc/cbc.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace Botan { @@ -39,13 +40,16 @@ class BOTAN_DLL CBC_Encryption : public Keyed_Filter ~CBC_Encryption() { delete padder; } private: - void write(const byte[], u32bit); + void cbc_encrypt(const byte input[], u32bit input_length); + void cbc_final(const byte input[], u32bit input_length); + + void write(const byte input[], u32bit input_length); void end_msg(); BlockCipher* cipher; const BlockCipherModePaddingMethod* padder; - SecureVector buffer, state; - u32bit position; + Buffered_Operation buf_op; + SecureVector state; }; /* @@ -73,13 +77,16 @@ class BOTAN_DLL CBC_Decryption : public Keyed_Filter ~CBC_Decryption() { delete padder; } private: + void cbc_decrypt(const byte input[], u32bit input_length); + void cbc_final(const byte input[], u32bit input_length); + void write(const byte[], u32bit); void end_msg(); BlockCipher* cipher; const BlockCipherModePaddingMethod* padder; - SecureVector buffer, state, temp; - u32bit position; + Buffered_Operation buf_op; + SecureVector state, temp; }; } -- cgit v1.2.3