diff options
Diffstat (limited to 'src/modes')
-rw-r--r-- | src/modes/xts/info.txt | 15 | ||||
-rw-r--r-- | src/modes/xts/xts.cpp | 344 | ||||
-rw-r--r-- | src/modes/xts/xts.h | 76 |
3 files changed, 435 insertions, 0 deletions
diff --git a/src/modes/xts/info.txt b/src/modes/xts/info.txt new file mode 100644 index 000000000..30d8f3496 --- /dev/null +++ b/src/modes/xts/info.txt @@ -0,0 +1,15 @@ +realname "XTS block cipher mode" + +define XTS + +load_on auto + +<add> +xts.cpp +xts.h +</add> + +<requires> +modes +</requires> + diff --git a/src/modes/xts/xts.cpp b/src/modes/xts/xts.cpp new file mode 100644 index 000000000..8819c85dc --- /dev/null +++ b/src/modes/xts/xts.cpp @@ -0,0 +1,344 @@ +/* +* XTS Mode +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/xts.h> +#include <botan/xor_buf.h> +#include <algorithm> +#include <stdexcept> + +namespace Botan { + +namespace { + +void poly_double(byte tweak[], u32bit size) + { + const byte polynomial = 0x87; // for 128 bit ciphers + + byte carry = 0; + for(u32bit i = 0; i != size; ++i) + { + byte carry2 = (tweak[i] >> 7); + tweak[i] = (tweak[i] << 1) | carry; + carry = carry2; + } + + if(carry) + tweak[0] ^= polynomial; + } + +} + +/* +* XTS_Encryption constructor +*/ +XTS_Encryption::XTS_Encryption(BlockCipher* ciph) : cipher(ciph) + { + if(cipher->BLOCK_SIZE != 16) + throw std::invalid_argument("Bad cipher for XTS: " + cipher->name()); + + cipher2 = cipher->clone(); + buffer.create(cipher->BLOCK_SIZE); + tweak.create(cipher->BLOCK_SIZE); + buffer.create(2 * cipher->BLOCK_SIZE); + position = 0; + } + +/* +* XTS_Encryption constructor +*/ +XTS_Encryption::XTS_Encryption(BlockCipher* ciph, + const SymmetricKey& key, + const InitializationVector& iv) : cipher(ciph) + { + if(cipher->BLOCK_SIZE != 16) + throw std::invalid_argument("Bad cipher for XTS: " + cipher->name()); + + cipher2 = cipher->clone(); + buffer.create(cipher->BLOCK_SIZE); + tweak.create(cipher->BLOCK_SIZE); + buffer.create(2 * cipher->BLOCK_SIZE); + position = 0; + + set_key(key); + set_iv(iv); + } + +/* +* Return the name +*/ +std::string XTS_Encryption::name() const + { + return (cipher->name() + "/XTS"); + } + +/* +* Set new tweak +*/ +void XTS_Encryption::set_iv(const InitializationVector& iv) + { + if(iv.length() != tweak.size()) + throw Invalid_IV_Length(name(), iv.length()); + + tweak = iv.bits_of(); + cipher2->encrypt(tweak); + } + +void XTS_Encryption::set_key(const SymmetricKey& key) + { + u32bit 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); + } + +void XTS_Encryption::encrypt(const byte block[]) + { + /* + * We can always use the first 16 bytes of buffer as temp space, + * since either the input block is buffer (in which case this is + * just buffer ^= tweak) or it not, in which case we already read + * and used the data there and are processing new input. Kind of + * subtle/nasty, but saves allocating a distinct temp buf. + */ + + xor_buf(buffer, block, tweak, cipher->BLOCK_SIZE); + cipher->encrypt(buffer); + xor_buf(buffer, tweak, cipher->BLOCK_SIZE); + + poly_double(tweak, cipher->BLOCK_SIZE); + + send(buffer, cipher->BLOCK_SIZE); + } + +/* +* Encrypt in XTS mode +*/ +void XTS_Encryption::write(const byte input[], u32bit length) + { + const u32bit BLOCK_SIZE = cipher->BLOCK_SIZE; + + u32bit copied = std::min(buffer.size() - position, length); + buffer.copy(position, input, copied); + length -= copied; + input += copied; + position += copied; + + if(length == 0) return; + + encrypt(buffer); + if(length > BLOCK_SIZE) + { + encrypt(buffer + BLOCK_SIZE); + while(length > buffer.size()) + { + encrypt(input); + length -= BLOCK_SIZE; + input += BLOCK_SIZE; + } + position = 0; + } + else + { + copy_mem(buffer.begin(), buffer + BLOCK_SIZE, BLOCK_SIZE); + position = BLOCK_SIZE; + } + buffer.copy(position, input, length); + position += length; + } + +/* +* Finish encrypting in XTS mode +*/ +void XTS_Encryption::end_msg() + { + const u32bit BLOCK_SIZE = cipher->BLOCK_SIZE; + + if(position < BLOCK_SIZE) + throw Exception("XTS_Encryption: insufficient data to encrypt"); + else if(position == BLOCK_SIZE) + { + encrypt(buffer); + } + else if(position == 2*BLOCK_SIZE) + { + encrypt(buffer); + encrypt(buffer + BLOCK_SIZE); + } + else + { // steal ciphertext + xor_buf(buffer, tweak, cipher->BLOCK_SIZE); + cipher->encrypt(buffer); + xor_buf(buffer, tweak, cipher->BLOCK_SIZE); + + poly_double(tweak, cipher->BLOCK_SIZE); + + for(u32bit i = 0; i != position - cipher->BLOCK_SIZE; ++i) + std::swap(buffer[i], buffer[i + cipher->BLOCK_SIZE]); + + xor_buf(buffer, tweak, cipher->BLOCK_SIZE); + cipher->encrypt(buffer); + xor_buf(buffer, tweak, cipher->BLOCK_SIZE); + + send(buffer, position); + } + } + +/* +* XTS_Decryption constructor +*/ +XTS_Decryption::XTS_Decryption(BlockCipher* ciph) + { + cipher = ciph; + cipher2 = ciph->clone(); + buffer.create(cipher->BLOCK_SIZE); + tweak.create(cipher->BLOCK_SIZE); + buffer.create(2 * cipher->BLOCK_SIZE); + position = 0; + } + +/* +* XTS_Decryption constructor +*/ +XTS_Decryption::XTS_Decryption(BlockCipher* ciph, + const SymmetricKey& key, + const InitializationVector& iv) + { + cipher = ciph; + cipher2 = ciph->clone(); + buffer.create(cipher->BLOCK_SIZE); + tweak.create(cipher->BLOCK_SIZE); + buffer.create(2 * cipher->BLOCK_SIZE); + position = 0; + + set_key(key); + set_iv(iv); + } + +/* +* Return the name +*/ +std::string XTS_Decryption::name() const + { + return (cipher->name() + "/XTS"); + } + +/* +* Set new tweak +*/ +void XTS_Decryption::set_iv(const InitializationVector& iv) + { + if(iv.length() != tweak.size()) + throw Invalid_IV_Length(name(), iv.length()); + + tweak = iv.bits_of(); + cipher2->encrypt(tweak); + } + +void XTS_Decryption::set_key(const SymmetricKey& key) + { + u32bit 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 a block +*/ +void XTS_Decryption::decrypt(const byte block[]) + { + xor_buf(buffer, block, tweak, cipher->BLOCK_SIZE); + cipher->decrypt(buffer); + xor_buf(buffer, tweak, cipher->BLOCK_SIZE); + + poly_double(tweak, cipher->BLOCK_SIZE); + + send(buffer, cipher->BLOCK_SIZE); + } + +/* +* Decrypt in XTS mode +*/ +void XTS_Decryption::write(const byte input[], u32bit length) + { + const u32bit BLOCK_SIZE = cipher->BLOCK_SIZE; + + u32bit copied = std::min(buffer.size() - position, length); + buffer.copy(position, input, copied); + length -= copied; + input += copied; + position += copied; + + if(length == 0) return; + + decrypt(buffer); + if(length > BLOCK_SIZE) + { + decrypt(buffer + BLOCK_SIZE); + while(length > 2*BLOCK_SIZE) + { + decrypt(input); + length -= BLOCK_SIZE; + input += BLOCK_SIZE; + } + position = 0; + } + else + { + copy_mem(buffer.begin(), buffer + BLOCK_SIZE, BLOCK_SIZE); + position = BLOCK_SIZE; + } + buffer.copy(position, input, length); + position += length; + } + +/* +* Finish decrypting in XTS mode +*/ +void XTS_Decryption::end_msg() + { + const u32bit BLOCK_SIZE = cipher->BLOCK_SIZE; + + if(position < BLOCK_SIZE) + throw Exception("XTS_Decryption: insufficient data to decrypt"); + else if(position == BLOCK_SIZE) + { + decrypt(buffer); + } + else if(position == 2*BLOCK_SIZE) + { + decrypt(buffer); + decrypt(buffer + BLOCK_SIZE); + } + else + { + SecureVector<byte> tweak2 = tweak; + + poly_double(tweak2, cipher->BLOCK_SIZE); + + xor_buf(buffer, tweak2, cipher->BLOCK_SIZE); + cipher->decrypt(buffer); + xor_buf(buffer, tweak2, cipher->BLOCK_SIZE); + + for(u32bit i = 0; i != position - cipher->BLOCK_SIZE; ++i) + std::swap(buffer[i], buffer[i + cipher->BLOCK_SIZE]); + + xor_buf(buffer, tweak, cipher->BLOCK_SIZE); + cipher->decrypt(buffer); + xor_buf(buffer, tweak, cipher->BLOCK_SIZE); + + send(buffer, position); + } + } + +} diff --git a/src/modes/xts/xts.h b/src/modes/xts/xts.h new file mode 100644 index 000000000..01558175b --- /dev/null +++ b/src/modes/xts/xts.h @@ -0,0 +1,76 @@ +/* +* 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/basefilt.h> +#include <botan/block_cipher.h> + +namespace Botan { + +/* +* XTS Encryption +*/ +class BOTAN_DLL XTS_Encryption : public Keyed_Filter + { + public: + void set_key(const SymmetricKey& key); + void set_iv(const InitializationVector& iv); + + 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[], u32bit); + void end_msg(); + void encrypt(const byte block[]); + + BlockCipher* cipher; + BlockCipher* cipher2; + SecureVector<byte> tweak; + SecureVector<byte> buffer; + u32bit position; + }; + +/* +* XTS Decryption +*/ +class BOTAN_DLL XTS_Decryption : public Keyed_Filter + { + public: + void set_key(const SymmetricKey& key); + void set_iv(const InitializationVector& iv); + + std::string name() const; + + XTS_Decryption(BlockCipher* ciph); + + XTS_Decryption(BlockCipher* ciph, + const SymmetricKey& key, + const InitializationVector& iv); + private: + void write(const byte[], u32bit); + void end_msg(); + void decrypt(const byte[]); + + BlockCipher* cipher; + BlockCipher* cipher2; + SecureVector<byte> tweak; + SecureVector<byte> buffer; + u32bit position; + }; + +} + +#endif |