XTS Mode
Documentation |
#include <cryptopp/xts.h>
|
XTS Mode is a block cipher mode of operation intended for use on storage devices to encrypt disk sectors. XTS mode was originally specified by the IEEE in P1619, and NIST standardized it in SP800-38E.
XTS is only defined when using AES, but Crypto++ allows wider block ciphers if you build the library with CRYPTOPP_XTS_WIDE_BLOCK_CIPHERS
defined to non-zero. Be careful when using XTS with wider block ciphers. According to Phillip Rogaway in Evaluation of Some Blockcipher Modes of Operation, the narrow width of the underlying PRP and the poor treatment of fractional final blocks are problems.
The mode does not require a multiple of a block size or padding, which means there is no cipher text expansion. XTS does require at least 16 bytes of plain text. The 16-byte requirement comes from the fact the mode steals cipher text from a previous encrypted block when there are tail bytes present.
XTS mode provides confidentiality only. The mode lacks an authentication tag to detect tampering. Some of the risk is eliminated because the encrypted data is in local storage, and not sent across the network.
Crypto++ offers several modes of operation, including ECB, CBC, OFB, CFB, CBC-CTS, CTR, XTS, CCM, EAX, GCM and OCB.
Crypto++ does not provide a way to retrieve the current IV or counter used for encryption or decryption. If you need the current IV or counter then you need to manage it yourself. Some ciphers allow you to seek a number of bytes or blocks in the stream.
If you are used to working in languages like Java or libraries like OpenSSL, then you might want to visit the Init-Update-Final wiki page. Crypto++ provides the transformation model, but its not obvious because its often shrouded behind Pipelines.
Note: if your project is using encryption alone to secure your data, encryption alone is usually not enough. Please take a moment to read Authenticated Encryption and consider using an algorithm or mode like CCM, GCM, EAX, ChaCha20Poly1305 or XChaCha20Poly1305.
Sample Program
There are three sample programs for XTS mode. The samples use filters in a pipeline. The first dumps the key sizes. The second sample shows the basic use of a pipeline. The third shows how to manually insert into a filter. Pipelining is a high level abstraction and it handles buffering input, buffering output and padding for you.
If you are benchmarking then you may want to visit Benchmarks | Sample Program . It shows you how to use StreamTransformation
and its ProcessString
method to process multiple blocks at a time. ProcessString
eventually calls BlockTransformation
and ProcessBlock
. Calling a cipher's ProcessString
or ProcessBlock
eventually call ProcessAndXorBlock
or AdvancedProcessBlocks
, and they are the lowest level API you can use.
The first program below shows key lengths and block sizes.
XTS<AES>::Encryption enc; cout << "key length: " << enc.DefaultKeyLength() << endl; cout << "key length (min): " << enc.MinKeyLength() << endl; cout << "key length (max): " << enc.MaxKeyLength() << endl; cout << "block size: " << enc.BlockSize() << endl;
Running the program results in the following.
key length: 32 key length (min): 32 key length (max): 64 block size: 16
The second program below demonstrates using AES/XTS in a pipeline.
using namespace CryptoPP; AutoSeededRandomPool prng; SecByteBlock key(32), iv(16); prng.GenerateBlock( key, key.size() ); prng.GenerateBlock( iv, iv.size() ); std::string plain = "XTS Mode Test"; std::string cipher, encoded, recovered; /*********************************\ \*********************************/ try { std::cout << "plain text: " << plain << std::endl; XTS_Mode< AES >::Encryption enc; enc.SetKeyWithIV( key, key.size(), iv ); // The StreamTransformationFilter adds padding // as requiredec. ECB and XTS Mode must be padded // to the block size of the cipher. StringSource ss( plain, true, new StreamTransformationFilter( enc, new StringSink( cipher ), StreamTransformationFilter::NO_PADDING ) // StreamTransformationFilter ); // StringSource } catch( const CryptoPP::Exception& ex ) { std::cerr << ex.what() << std::endl; exit(1); } /*********************************\ \*********************************/ // Pretty print cipher text StringSource ss( cipher, true, new HexEncoder( new StringSink( encoded ) ) // HexEncoder ); // StringSource std::cout << "cipher text: " << encoded << std::endl; /*********************************\ \*********************************/ try { XTS_Mode< AES >::Decryption dec; dec.SetKeyWithIV( key, key.size(), iv ); // The StreamTransformationFilter removes // padding as requiredec. StringSource ss( cipher, true, new StreamTransformationFilter( dec, new StringSink( recovered ), StreamTransformationFilter::NO_PADDING ) // StreamTransformationFilter ); // StringSource std::cout << "recovered text: " << recovered << std::endl; } catch( const CryptoPP::Exception& ex ) { std::cerr << ex.what() << std::endl; exit(1); }
A typical output is shown below. Note that each run will produce different results because the key and initialization vector are randomly generated.
$ ./test.exe plain text: AES/XTS block cipher test key: 5ED5A947D474DD2C763E537C82BCB9439AB05BD4D57DFA1D549F994F3AE3A5EE iv: E063B675FC03F687B50329B36251E725 cipher text: 69D1412A1BCBC90618A4976BAD5D1793549C7107032F332815 recovered text: AES/XTS block cipher test
When using a StreamTransformationFilter
, the filter will pad the plain text as required. XTS does not need padding so you should use the NOPADDING
flag.
StringSource ss( plain, true, new StreamTransformationFilter( e, new StringSink( cipher ), StreamTransformationFilter::NO_PADDING ) // StreamTransformationFilter ); // StringSource
If you use a short string for the message, then the XTS object will throw an InvalidArgument
exception.
$ ./test.exe plain text: XTS mode test XTS: message is too short for ciphertext stealing
To manually insert bytes into the filter, perform multiple Put
s. Though Get
is used below, a StringSink
could easily be attached and save the administrivia.
const size_t SIZE = 16 * 4; string plain(SIZE, 0x00); for(size_t i = 0; i < plain.size(); i++) plain[i] = 'A' + (i%26); ... XTS_Mode < AES >::Encryption encryption(key, key.size(), iv); StreamTransformationFilter encryptor(encryption, NULL); for(size_t j = 0; j < plain.size(); j++) encryptor.Put((byte)plain[j]); encryptor.MessageEnd(); size_t ready = encryptor.MaxRetrievable(); string cipher(ready, 0x00); encryptor.Get((byte*) &cipher[0], cipher.size());
Downloads
xtstest.zip - Demonstrates encryption and decryption using AES in XTS mode with filters