Add XTS block cipher mode of operation (GH #891, PR #892)

This commit is contained in:
Jeffrey Walton 2019-10-12 07:14:38 -04:00 committed by GitHub
parent 85ecff4609
commit 76c29eadaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 8772 additions and 21 deletions

View File

@ -410,6 +410,8 @@ xtr.cpp
xtr.h
xtrcrypt.cpp
xtrcrypt.h
xts.cpp
xts.h
zdeflate.cpp
zdeflate.h
zinflate.cpp
@ -576,6 +578,7 @@ TestVectors/vmac.txt
TestVectors/wake.txt
TestVectors/whrlpool.txt
TestVectors/xchacha.txt
TestVectors/xts.txt
TestPrograms/test_32bit.cxx
TestPrograms/test_64bit.cxx
TestPrograms/test_arm_acle.cxx

8204
TestVectors/xts.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -167,6 +167,9 @@ void Benchmark2(double t, double hertz)
BenchMarkByName<SymmetricCipher>("AES/CBC", 16);
BenchMarkByName<SymmetricCipher>("AES/CBC", 24);
BenchMarkByName<SymmetricCipher>("AES/CBC", 32);
BenchMarkByName<SymmetricCipher>("AES/XTS", 32);
BenchMarkByName<SymmetricCipher>("AES/XTS", 48);
BenchMarkByName<SymmetricCipher>("AES/XTS", 64);
BenchMarkByName<SymmetricCipher>("AES/OFB", 16);
BenchMarkByName<SymmetricCipher>("AES/CFB", 16);
BenchMarkByName<SymmetricCipher>("AES/ECB", 16);

View File

@ -5,14 +5,17 @@
#ifndef CRYPTOPP_IMPORTS
#include "cmac.h"
#include "misc.h"
NAMESPACE_BEGIN(CryptoPP)
ANONYMOUS_NAMESPACE_BEGIN
static void MulU(byte *k, unsigned int length)
using CryptoPP::byte;
using CryptoPP::IsPowerOf2;
void MulU(byte *k, unsigned int len)
{
byte carry = 0;
for (int i=length-1; i>=1; i-=2)
for (int i=len-1; i>=1; i-=2)
{
byte carry2 = k[i] >> 7;
k[i] += k[i] + carry;
@ -20,9 +23,22 @@ static void MulU(byte *k, unsigned int length)
k[i-1] += k[i-1] + carry2;
}
#ifndef CRYPTOPP_CMAC_WIDE_BLOCK_CIPHERS
CRYPTOPP_ASSERT(len == 16);
if (carry)
{
switch (length)
k[15] ^= 0x87;
return;
}
#else
CRYPTOPP_ASSERT(IsPowerOf2(len));
CRYPTOPP_ASSERT(len >= 8);
CRYPTOPP_ASSERT(len <= 128);
if (carry)
{
switch (len)
{
case 8:
k[7] ^= 0x1b;
@ -50,11 +66,16 @@ static void MulU(byte *k, unsigned int length)
k[127] ^= 0x43;
break;
default:
throw InvalidArgument("CMAC: " + IntToString(length) + " is not a supported cipher block size");
CRYPTOPP_ASSERT(0);
}
}
#endif // CRYPTOPP_CMAC_WIDE_BLOCK_CIPHERS
}
ANONYMOUS_NAMESPACE_END
NAMESPACE_BEGIN(CryptoPP)
void CMAC_Base::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs &params)
{
BlockCipher &cipher = AccessCipher();

8
cmac.h
View File

@ -10,6 +10,13 @@
#include "seckey.h"
#include "secblock.h"
/// \brief Enable CMAC and wide block ciphers
/// \details CMAC is only defined for AES. The library can support wide
/// block ciphers like Kaylna and Threefish since we know the polynomials.
#ifndef CRYPTOPP_CMAC_WIDE_BLOCK_CIPHERS
# define CRYPTOPP_CMAC_WIDE_BLOCK_CIPHERS 1
#endif // CRYPTOPP_CMAC_WIDE_BLOCK_CIPHERS
NAMESPACE_BEGIN(CryptoPP)
/// \brief CMAC base implementation
@ -19,7 +26,6 @@ class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CMAC_Base : public MessageAuthenticationCo
public:
virtual ~CMAC_Base() {}
CMAC_Base() : m_counter(0) {}
void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs &params);

View File

@ -83,7 +83,7 @@ LIB_SRCS = \
sm4_simd.cpp sosemanuk.cpp speck.cpp speck128_simd.cpp speck64_simd.cpp \
square.cpp squaretb.cpp sse_simd.cpp strciphr.cpp tea.cpp tftables.cpp \
threefish.cpp tiger.cpp tigertab.cpp ttmac.cpp tweetnacl.cpp twofish.cpp \
vmac.cpp wake.cpp whrlpool.cpp xed25519.cpp xtr.cpp xtrcrypt.cpp \
vmac.cpp wake.cpp whrlpool.cpp xed25519.cpp xtr.cpp xtrcrypt.cpp xts.cpp \
zdeflate.cpp zinflate.cpp zlib.cpp
LIB_OBJS = \
@ -114,7 +114,7 @@ LIB_OBJS = \
sm4_simd.obj sosemanuk.obj speck.obj speck128_simd.obj speck64_simd.obj \
square.obj squaretb.obj sse_simd.obj strciphr.obj tea.obj tftables.obj \
threefish.obj tiger.obj tigertab.obj ttmac.obj tweetnacl.obj twofish.obj \
vmac.obj wake.obj whrlpool.obj xed25519.obj xtr.obj xtrcrypt.obj \
vmac.obj wake.obj whrlpool.obj xed25519.obj xtr.obj xtrcrypt.obj xts.obj \
zdeflate.obj zinflate.obj zlib.obj
ASM_OBJS = \

View File

@ -346,6 +346,7 @@
<ClCompile Include="xed25519.cpp" />
<ClCompile Include="xtr.cpp" />
<ClCompile Include="xtrcrypt.cpp" />
<ClCompile Include="xts.cpp" />
<ClCompile Include="zdeflate.cpp" />
<ClCompile Include="zinflate.cpp" />
<ClCompile Include="zlib.cpp" />
@ -568,6 +569,7 @@
<ClInclude Include="xed25519.h" />
<ClInclude Include="xtr.h" />
<ClInclude Include="xtrcrypt.h" />
<ClInclude Include="xts.h" />
<ClInclude Include="zdeflate.h" />
<ClInclude Include="zinflate.h" />
<ClInclude Include="zlib.h" />

View File

@ -518,6 +518,9 @@
<ClCompile Include="xtrcrypt.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="xts.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="zdeflate.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -1053,6 +1056,9 @@
<ClInclude Include="xtrcrypt.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="xts.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="zdeflate.h">
<Filter>Header Files</Filter>
</ClInclude>

View File

@ -213,7 +213,7 @@ size_t CBC_CTS_Encryption::ProcessLastBlock(byte *outString, size_t outLength, c
const size_t used = inLength;
const unsigned int blockSize = BlockSize();
if (inLength <= BlockSize())
if (inLength <= blockSize)
{
if (!m_stolenIV)
throw InvalidArgument("CBC_Encryption: message is too short for ciphertext stealing");

View File

@ -255,8 +255,10 @@ public:
unsigned int MandatoryBlockSize() const {return BlockSize();}
bool IsRandomAccess() const {return false;}
bool IsSelfInverting() const {return false;}
bool IsForwardTransformation() const {return m_cipher->IsForwardTransformation();}
void Resynchronize(const byte *iv, int length=-1) {memcpy_s(m_register, m_register.size(), iv, ThrowIfInvalidIVLength(length));}
bool IsForwardTransformation() const
{return m_cipher->IsForwardTransformation();}
void Resynchronize(const byte *iv, int length=-1)
{memcpy_s(m_register, m_register.size(), iv, ThrowIfInvalidIVLength(length));}
protected:
bool RequireAlignedInput() const {return true;}

View File

@ -21,6 +21,7 @@
#include "ccm.h"
#include "gcm.h"
#include "eax.h"
#include "xts.h"
#include "twofish.h"
#include "serpent.h"
#include "cast.h"
@ -67,6 +68,7 @@ void RegisterFactories4()
RegisterSymmetricCipherDefaultFactories<CFB_Mode<AES> >();
RegisterSymmetricCipherDefaultFactories<OFB_Mode<AES> >();
RegisterSymmetricCipherDefaultFactories<CTR_Mode<AES> >();
RegisterSymmetricCipherDefaultFactories<XTS_Mode<AES> >("AES/XTS");
RegisterAuthenticatedSymmetricCipherDefaultFactories<CCM<AES> >();
RegisterAuthenticatedSymmetricCipherDefaultFactories<GCM<AES> >();

View File

@ -1010,15 +1010,17 @@ bool Validate(int alg, bool thorough)
case 80: result = ValidateVMAC(); break;
case 81: result = ValidateCCM(); break;
case 82: result = ValidateGCM(); break;
case 83: result = ValidateCMAC(); break;
case 84: result = ValidateSM3(); break;
case 85: result = ValidateBLAKE2s(); break;
case 86: result = ValidateBLAKE2b(); break;
case 87: result = ValidatePoly1305(); break;
case 88: result = ValidateSipHash(); break;
case 89: result = ValidateHashDRBG(); break;
case 90: result = ValidateHmacDRBG(); break;
case 91: result = ValidateNaCl(); break;
case 83: result = ValidateXTS(); break;
case 84: result = ValidateCMAC(); break;
case 85: result = ValidateSM3(); break;
case 86: result = ValidateBLAKE2s(); break;
case 87: result = ValidateBLAKE2b(); break;
case 88: result = ValidatePoly1305(); break;
case 89: result = ValidateSipHash(); break;
case 90: result = ValidateHashDRBG(); break;
case 91: result = ValidateHmacDRBG(); break;
case 92: result = ValidateNaCl(); break;
case 100: result = ValidateCHAM(); break;
case 101: result = ValidateSIMECK(); break;
case 102: result = ValidateSIMON(); break;

View File

@ -171,6 +171,7 @@ bool ValidateAll(bool thorough)
pass=ValidateVMAC() && pass;
pass=ValidateCCM() && pass;
pass=ValidateGCM() && pass;
pass=ValidateXTS() && pass;
pass=ValidateCMAC() && pass;
pass=RunTestDataFile("TestVectors/eax.txt") && pass;

View File

@ -1860,6 +1860,12 @@ bool ValidateGCM()
return RunTestDataFile("TestVectors/gcm.txt", MakeParameters(Name::TableSize(), (int)64*1024)) && pass;
}
bool ValidateXTS()
{
std::cout << "\nAES/XTS validation suite running...\n";
return RunTestDataFile("TestVectors/xts.txt");
}
bool ValidateCMAC()
{
std::cout << "\nCMAC validation suite running...\n";

View File

@ -117,6 +117,7 @@ bool ValidateSosemanuk();
bool ValidateVMAC();
bool ValidateCCM();
bool ValidateGCM();
bool ValidateXTS();
bool ValidateCMAC();
bool ValidateBBS();

336
xts.cpp Normal file
View File

@ -0,0 +1,336 @@
// xts.cpp - written and placed in the public domain by Jeffrey Walton
#include "pch.h"
#include "xts.h"
#include "misc.h"
#include "modes.h"
#if defined(CRYPTOPP_DEBUG)
# include "aes.h"
# include "threefish.h"
#endif
ANONYMOUS_NAMESPACE_BEGIN
using CryptoPP::byte;
using CryptoPP::word32;
using CryptoPP::word64;
using CryptoPP::GetWord;
using CryptoPP::PutWord;
using CryptoPP::IsPowerOf2;
using CryptoPP::BIG_ENDIAN_ORDER;
using CryptoPP::LITTLE_ENDIAN_ORDER;
// Borrowed from CMAC, but little-endian representation
inline void GF_Double(byte *k, unsigned int len)
{
#if defined(_LP64) || defined(__LP64__)
word64 carry = 0, x;
for (size_t i=0, idx=0; i<len/8; ++i, idx+=8)
{
x = GetWord<word64>(false, LITTLE_ENDIAN_ORDER, k+idx);
word64 y = (x >> 63); x = (x << 1) + carry;
PutWord<word64>(false, LITTLE_ENDIAN_ORDER, k+idx, x);
carry = y;
}
#else
word32 carry = 0, x;
for (size_t i=0, idx=0; i<len/4; ++i, idx+=4)
{
x = GetWord<word32>(false, LITTLE_ENDIAN_ORDER, k+idx);
word32 y = (x >> 31); x = (x << 1) + carry;
PutWord<word32>(false, LITTLE_ENDIAN_ORDER, k+idx, x);
carry = y;
}
#endif
#if CRYPTOPP_XTS_WIDE_BLOCK_CIPHERS
CRYPTOPP_ASSERT(IsPowerOf2(len));
CRYPTOPP_ASSERT(len >= 8);
CRYPTOPP_ASSERT(len <= 128);
// Special case the dominant case
if (carry && len == 16)
{
k[0] ^= 0x87;
return;
}
if (carry)
{
switch (len)
{
case 8:
{
const size_t LEIDX = 8-1;
k[LEIDX-7] ^= 0x1b;
break;
}
case 16:
{
const size_t LEIDX = 16-1;
k[LEIDX-15] ^= 0x87;
break;
}
case 32:
{
// https://crypto.stackexchange.com/q/9815/10496
// Polynomial x^256 + x^10 + x^5 + x^2 + 1
const size_t LEIDX = 32-1;
k[LEIDX-30] ^= 4;
k[LEIDX-31] ^= 0x25;
break;
}
case 64:
{
// https://crypto.stackexchange.com/q/9815/10496
// Polynomial x^512 + x^8 + x^5 + x^2 + 1
const size_t LEIDX = 64-1;
k[LEIDX-62] ^= 1;
k[LEIDX-63] ^= 0x25;
break;
}
case 128:
{
// https://crypto.stackexchange.com/q/9815/10496
// Polynomial x^1024 + x^19 + x^6 + x + 1
const size_t LEIDX = 128-1;
k[LEIDX-125] ^= 8;
k[LEIDX-126] ^= 0x00;
k[LEIDX-127] ^= 0x43;
break;
}
default:
CRYPTOPP_ASSERT(0);
}
}
#else
CRYPTOPP_ASSERT(len == 16);
if (carry)
{
k[0] ^= 0x87;
return;
}
#endif // CRYPTOPP_XTS_WIDE_BLOCK_CIPHERS
}
#if defined(CRYPTOPP_DEBUG) && !defined(CRYPTOPP_DOXYGEN_PROCESSING)
using CryptoPP::AES;
using CryptoPP::XTS_Mode;
using CryptoPP::Threefish512;
void Modes_TestInstantiations()
{
XTS_Mode<AES>::Encryption m0;
XTS_Mode<AES>::Decryption m1;
XTS_Mode<AES>::Encryption m2;
XTS_Mode<AES>::Decryption m3;
#if CRYPTOPP_XTS_WIDE_BLOCK_CIPHERS
XTS_Mode<Threefish512>::Encryption m4;
XTS_Mode<Threefish512>::Decryption m5;
#endif
}
#endif
ANONYMOUS_NAMESPACE_END
NAMESPACE_BEGIN(CryptoPP)
void XTS_ModeBase::SetKey(const byte *key, size_t length, const NameValuePairs &params)
{
CRYPTOPP_ASSERT(length % 2 == 0);
const size_t klen = length/2;
AccessBlockCipher().SetKey(key+0, klen, params);
AccessTweakCipher().SetKey(key+klen, klen, params);
ResizeBuffers();
size_t ivLength;
const byte *iv = GetIVAndThrowIfInvalid(params, ivLength);
Resynchronize(iv, (int)ivLength);
}
void XTS_ModeBase::Resynchronize(const byte *iv, int ivLength)
{
BlockOrientedCipherModeBase::Resynchronize(iv, ivLength);
GetTweakCipher().ProcessBlock(m_register);
}
void XTS_ModeBase::Resynchronize(word64 sector, ByteOrder order)
{
SecByteBlock iv(GetTweakCipher().BlockSize());
PutWord<word64>(false, order, iv, sector);
std::memset(iv+8, 0x00, iv.size()-8);
BlockOrientedCipherModeBase::Resynchronize(iv, iv.size());
GetTweakCipher().ProcessBlock(m_register);
}
void XTS_ModeBase::ResizeBuffers()
{
BlockOrientedCipherModeBase::ResizeBuffers();
m_workspace.New(GetBlockCipher().BlockSize());
}
void XTS_ModeBase::ProcessData(byte *outString, const byte *inString, size_t length)
{
const unsigned int blockSize = GetBlockCipher().BlockSize();
// data unit is multiple of 16 bytes
CRYPTOPP_ASSERT(length % blockSize == 0);
// now encrypt the data unit, AES_BLK_BYTES at a time
for (size_t i=0; i<length; i+=blockSize)
{
// merge the tweak into the input block
xorbuf(m_workspace, inString+i, m_register, blockSize);
// encrypt one block, merge the tweak into the output block
GetBlockCipher().AdvancedProcessBlocks(m_workspace, m_register, outString+i, blockSize, 0);
// Multiply T by alpha
GF_Double(m_register, m_register.size());
}
}
size_t XTS_ModeBase::ProcessLastBlock(byte *outString, size_t outLength, const byte *inString, size_t inLength)
{
if (IsForwardTransformation())
return ProcessLastPlainBlock(outString, outLength, inString, inLength);
else
return ProcessLastCipherBlock(outString, outLength, inString, inLength);
}
size_t XTS_ModeBase::ProcessLastPlainBlock(byte *outString, size_t outLength, const byte *inString, size_t inLength)
{
// ensure output buffer is large enough
CRYPTOPP_ASSERT(outLength >= inLength);
// need at least a full AES block
CRYPTOPP_ASSERT(inLength >= BlockSize());
const unsigned int blockSize = GetBlockCipher().BlockSize();
const unsigned int blocks = inLength / blockSize;
const unsigned int tail = inLength % blockSize;
const size_t length = inLength;
if (tail == 0)
{
// Allow ProcessData to handle all the full blocks
ProcessData(outString, inString, inLength);
return inLength;
}
else if (blocks > 1)
{
// Allow ProcessData to handle full blocks except one
const size_t head = (blocks-1)*blockSize;
ProcessData(outString, inString, inLength-head);
outString += head; outLength -= head;
inString += head; inLength -= head;
}
///// handle the full block /////
// merge the tweak into the input block
xorbuf(m_workspace, inString, m_register, blockSize);
// encrypt one block, merge the tweak into the output block
GetBlockCipher().AdvancedProcessBlocks(m_workspace, m_register, outString, blockSize, 0);
// Multiply T by alpha
GF_Double(m_register, m_register.size());
///// handle final partial block /////
inString += blockSize;
outString += blockSize;
const size_t len = inLength-blockSize;
// copy in the final plaintext bytes
std::memcpy(m_workspace, inString, len);
// and copy out the final ciphertext bytes
std::memcpy(outString, outString-blockSize, len);
// "steal" ciphertext to complete the block
std::memcpy(m_workspace+len, outString-blockSize+len, blockSize-len);
// merge the tweak into the input block
xorbuf(m_workspace, m_register, blockSize);
// encrypt the final block, merge the tweak into the output block
GetBlockCipher().AdvancedProcessBlocks(m_workspace, m_register, outString-blockSize, blockSize, 0);
return length;
}
size_t XTS_ModeBase::ProcessLastCipherBlock(byte *outString, size_t outLength, const byte *inString, size_t inLength)
{
// ensure output buffer is large enough
CRYPTOPP_ASSERT(outLength >= inLength);
// need at least a full AES block
CRYPTOPP_ASSERT(inLength >= BlockSize());
const unsigned int blockSize = GetBlockCipher().BlockSize();
const unsigned int blocks = inLength / blockSize;
const unsigned int tail = inLength % blockSize;
const size_t length = inLength;
if (tail == 0)
{
// Allow ProcessData to handle all the full blocks
ProcessData(outString, inString, inLength);
return inLength;
}
else if (blocks > 1)
{
// Allow ProcessData to handle full blocks except one
const size_t head = (blocks-1)*blockSize;
ProcessData(outString, inString, inLength-head);
outString += head; outLength -= head;
inString += head; inLength -= head;
}
SecByteBlock poly1(m_register);
SecByteBlock poly2(m_register);
GF_Double(poly2, poly2.size());
///// handle final partial block /////
inString += blockSize;
outString += blockSize;
const size_t len = inLength-blockSize;
// merge the tweak into the input block
xorbuf(m_workspace, inString-blockSize, poly2, blockSize);
// encrypt one block, merge the tweak into the output block
GetBlockCipher().AdvancedProcessBlocks(m_workspace, poly2, m_workspace, blockSize, 0);
// copy in the final plaintext bytes
std::memcpy(outString-blockSize, inString, len);
// and copy out the final ciphertext bytes
std::memcpy(outString, m_workspace, len);
// "steal" ciphertext to complete the block
std::memcpy(outString-blockSize+len, m_workspace+len, blockSize-len);
///// handle the full previous block /////
inString -= blockSize;
outString -= blockSize;
// merge the tweak into the output block
xorbuf(m_workspace, outString, poly1, blockSize);
// encrypt one block, merge the tweak into the input block
GetBlockCipher().AdvancedProcessBlocks(m_workspace, poly1, outString, blockSize, 0);
return length;
}
NAMESPACE_END

156
xts.h Normal file
View File

@ -0,0 +1,156 @@
// xts.h - written and placed in the public domain by Jeffrey Walton
/// \file xts.h
/// \brief Classes for XTS block cipher mode of operation
/// \details XTS mode is a wide block mode defined by IEEE P1619-2008. NIST
/// SP-800-38E approves the mode for storage devices citing IEEE 1619-2007.
/// IEEE 1619-2007 provides both a reference implementation and test vectors.
/// The IEEE reference implementation fails to arrive at the expected result
/// for some test vectors.
/// \sa <A HREF="http://www.cryptopp.com/wiki/Modes_of_Operation">Modes of
/// Operation</A> on the Crypto++ wiki, <A
/// HREF="https://web.cs.ucdavis.edu/~rogaway/papers/modes.pdf"> Evaluation of Some
/// Blockcipher Modes of Operation</A>, <A
/// HREF="https://csrc.nist.gov/publications/detail/sp/800-38e/final">Recommendation
/// for Block Cipher Modes of Operation: The XTS-AES Mode for Confidentiality on
/// Storage Devices</A>, <A
/// HREF="http://libeccio.di.unisa.it/Crypto14/Lab/p1619.pdf">IEEE P1619-2007</A>
/// and <A HREF="https://crypto.stackexchange.com/q/74925/10496">IEEE P1619/XTS,
/// inconsistent reference implementation and test vectors</A>.
/// \since Crypto++ 8.3
#ifndef CRYPTOPP_XTS_MODE_H
#define CRYPTOPP_XTS_MODE_H
#include "cryptlib.h"
#include "secblock.h"
#include "modes.h"
#include "misc.h"
/// \brief Enable XTS for wide block ciphers
/// \details XTS is only defined for AES. The library can support wide
/// block ciphers like Kaylna and Threefish since we know the polynomials.
/// To enable wide block ciphers define <tt>CRYPTOPP_XTS_WIDE_BLOCK_CIPHERS</tt>
/// to non-zero. Note this is a library compile time define.
/// \details There is risk involved with using XTS with wider block ciphers.
/// According to Phillip Rogaway, "The narrow width of the underlying PRP and
/// the poor treatment of fractional final blocks are problems."
/// \sa <A HREF="https://web.cs.ucdavis.edu/~rogaway/papers/modes.pdf">Evaluation
/// of Some Blockcipher Modes of Operation</A>
/// \since Crypto++ 8.3
#ifndef CRYPTOPP_XTS_WIDE_BLOCK_CIPHERS
# define CRYPTOPP_XTS_WIDE_BLOCK_CIPHERS 0
#endif // CRYPTOPP_XTS_WIDE_BLOCK_CIPHERS
NAMESPACE_BEGIN(CryptoPP)
/// \brief XTS block cipher mode of operation default implementation
/// \since Crypto++ 8.3
class CRYPTOPP_NO_VTABLE XTS_ModeBase : public BlockOrientedCipherModeBase
{
public:
std::string AlgorithmName() const
{return GetBlockCipher().AlgorithmName() + "/XTS";}
std::string AlgorithmProvider() const
{return GetBlockCipher().AlgorithmProvider();}
size_t MinKeyLength() const
{return GetBlockCipher().MinKeyLength()*2;}
size_t MaxKeyLength() const
{return GetBlockCipher().MaxKeyLength()*2;}
size_t DefaultKeyLength() const
{return GetBlockCipher().DefaultKeyLength()*2;}
size_t GetValidKeyLength(size_t n) const
{return GetBlockCipher().GetValidKeyLength((n+1)/2);}
bool IsValidKeyLength(size_t keylength) const
{return keylength == GetValidKeyLength(keylength);}
unsigned int BlockSize() const
{return GetBlockCipher().BlockSize();}
unsigned int MinLastBlockSize() const
{return GetBlockCipher().BlockSize()+1;}
unsigned int OptimalDataAlignment() const
{return GetBlockCipher().OptimalDataAlignment();}
void SetKey(const byte *key, size_t length, const NameValuePairs &params = g_nullNameValuePairs);
IV_Requirement IVRequirement() const {return UNIQUE_IV;}
void Resynchronize(const byte *iv, int ivLength=-1);
void Resynchronize(word64 sector, ByteOrder order=BIG_ENDIAN_ORDER);
void ProcessData(byte *outString, const byte *inString, size_t length);
size_t ProcessLastBlock(byte *outString, size_t outLength, const byte *inString, size_t inLength);
protected:
virtual void ResizeBuffers();
inline size_t ProcessLastPlainBlock(byte *outString, size_t outLength, const byte *inString, size_t inLength);
inline size_t ProcessLastCipherBlock(byte *outString, size_t outLength, const byte *inString, size_t inLength);
virtual BlockCipher& AccessBlockCipher() = 0;
virtual BlockCipher& AccessTweakCipher() = 0;
const BlockCipher& GetBlockCipher() const
{return const_cast<XTS_ModeBase*>(this)->AccessBlockCipher();}
const BlockCipher& GetTweakCipher() const
{return const_cast<XTS_ModeBase*>(this)->AccessTweakCipher();}
SecByteBlock m_workspace;
};
/// \brief XTS block cipher mode of operation implementation details
/// \tparam CIPHER BlockCipher derived class or type
/// \since Crypto++ 8.3
template <class CIPHER>
class CRYPTOPP_NO_VTABLE XTS_Final : public XTS_ModeBase
{
public:
CRYPTOPP_STATIC_CONSTEXPR std::string CRYPTOPP_API StaticAlgorithmName()
{return std::string(CIPHER::StaticAlgorithmName()) + "/XTS";}
protected:
BlockCipher& AccessBlockCipher()
{return *m_cipher;}
BlockCipher& AccessTweakCipher()
{return m_tweaker;}
protected:
typename CIPHER::Encryption m_tweaker;
};
/// \brief XTS block cipher mode of operation
/// \tparam CIPHER BlockCipher derived class or type
/// \details XTS mode is a wide block mode defined by IEEE P1619-2008. NIST
/// SP-800-38E approves the mode for storage devices citing IEEE 1619-2007.
/// IEEE 1619-2007 provides both a reference implementation and test vectors.
/// The IEEE reference implementation fails to arrive at the expected result
/// for some test vectors.
/// \details XTS is only defined for AES. The library can support wide
/// block ciphers like Kaylna and Threefish since we know the polynomials.
/// There is risk involved with using XTS with wider block ciphers.
/// According to Phillip Rogaway, "The narrow width of the underlying PRP and
/// the poor treatment of fractional final blocks are problems." To enable
/// wide block cipher support define <tt>CRYPTOPP_XTS_WIDE_BLOCK_CIPHERS</tt> to
/// non-zero.
/// \sa <A HREF="http://www.cryptopp.com/wiki/Modes_of_Operation">Modes of
/// Operation</A> on the Crypto++ wiki, <A
/// HREF="https://web.cs.ucdavis.edu/~rogaway/papers/modes.pdf"> Evaluation of Some
/// Blockcipher Modes of Operation</A>, <A
/// HREF="https://csrc.nist.gov/publications/detail/sp/800-38e/final">Recommendation
/// for Block Cipher Modes of Operation: The XTS-AES Mode for Confidentiality on
/// Storage Devices</A>, <A
/// HREF="http://libeccio.di.unisa.it/Crypto14/Lab/p1619.pdf">IEEE P1619-2007</A>
/// and <A HREF="https://crypto.stackexchange.com/q/74925/10496">IEEE P1619/XTS,
/// inconsistent reference implementation and test vectors</A>.
/// \since Crypto++ 8.3
template <class CIPHER>
struct XTS : public CipherModeDocumentation
{
typedef CipherModeFinalTemplate_CipherHolder<typename CIPHER::Encryption, XTS_Final<CIPHER> > Encryption;
typedef CipherModeFinalTemplate_CipherHolder<typename CIPHER::Decryption, XTS_Final<CIPHER> > Decryption;
};
// C++03 lacks the mechanics to typedef a template
#define XTS_Mode XTS
NAMESPACE_END
#endif // CRYPTOPP_XTS_MODE_H