2017-01-27 12:05:45 +00:00
|
|
|
// modes.cpp - originally written and placed in the public domain by Wei Dai
|
2015-11-05 06:59:46 +00:00
|
|
|
|
|
|
|
#include "pch.h"
|
|
|
|
|
|
|
|
#ifndef CRYPTOPP_IMPORTS
|
|
|
|
|
|
|
|
#include "modes.h"
|
|
|
|
#include "misc.h"
|
|
|
|
|
2016-10-18 02:00:31 +00:00
|
|
|
#if defined(CRYPTOPP_DEBUG)
|
2015-11-05 06:59:46 +00:00
|
|
|
#include "des.h"
|
2016-10-18 02:00:31 +00:00
|
|
|
#endif
|
2015-11-05 06:59:46 +00:00
|
|
|
|
|
|
|
NAMESPACE_BEGIN(CryptoPP)
|
|
|
|
|
2016-10-18 02:00:31 +00:00
|
|
|
#if defined(CRYPTOPP_DEBUG) && !defined(CRYPTOPP_DOXYGEN_PROCESSING)
|
2015-11-05 06:59:46 +00:00
|
|
|
void Modes_TestInstantiations()
|
|
|
|
{
|
|
|
|
CFB_Mode<DES>::Encryption m0;
|
|
|
|
CFB_Mode<DES>::Decryption m1;
|
|
|
|
OFB_Mode<DES>::Encryption m2;
|
|
|
|
CTR_Mode<DES>::Encryption m3;
|
|
|
|
ECB_Mode<DES>::Encryption m4;
|
|
|
|
CBC_Mode<DES>::Encryption m5;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void CipherModeBase::ResizeBuffers()
|
|
|
|
{
|
|
|
|
m_register.New(m_cipher->BlockSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CFB_ModePolicy::Iterate(byte *output, const byte *input, CipherDir dir, size_t iterationCount)
|
|
|
|
{
|
2016-09-16 15:27:15 +00:00
|
|
|
CRYPTOPP_ASSERT(input);
|
|
|
|
CRYPTOPP_ASSERT(output);
|
2017-09-05 20:28:00 +00:00
|
|
|
CRYPTOPP_ASSERT(m_cipher->IsForwardTransformation()); // CFB mode needs the "encrypt" direction of the underlying block cipher, even to decrypt
|
2016-09-16 15:27:15 +00:00
|
|
|
CRYPTOPP_ASSERT(m_feedbackSize == BlockSize());
|
2015-11-05 06:59:46 +00:00
|
|
|
|
2015-11-18 20:32:28 +00:00
|
|
|
const unsigned int s = BlockSize();
|
2015-11-05 06:59:46 +00:00
|
|
|
if (dir == ENCRYPTION)
|
|
|
|
{
|
|
|
|
m_cipher->ProcessAndXorBlock(m_register, input, output);
|
2015-11-18 20:32:28 +00:00
|
|
|
if (iterationCount > 1)
|
|
|
|
m_cipher->AdvancedProcessBlocks(output, input+s, output+s, (iterationCount-1)*s, 0);
|
2017-09-05 20:28:00 +00:00
|
|
|
memcpy(m_register, output+(iterationCount-1)*s, s);
|
2015-11-05 06:59:46 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-09-05 20:28:00 +00:00
|
|
|
memcpy(m_temp, input+(iterationCount-1)*s, s); // make copy first in case of in-place decryption
|
2015-11-18 20:32:28 +00:00
|
|
|
if (iterationCount > 1)
|
|
|
|
m_cipher->AdvancedProcessBlocks(input, input+s, output+s, (iterationCount-1)*s, BlockTransformation::BT_ReverseDirection);
|
2015-11-05 06:59:46 +00:00
|
|
|
m_cipher->ProcessAndXorBlock(m_register, input, output);
|
2017-09-05 20:28:00 +00:00
|
|
|
memcpy(m_register, m_temp, s);
|
2015-11-05 06:59:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CFB_ModePolicy::TransformRegister()
|
|
|
|
{
|
2017-09-05 20:28:00 +00:00
|
|
|
CRYPTOPP_ASSERT(m_cipher->IsForwardTransformation()); // CFB mode needs the "encrypt" direction of the underlying block cipher, even to decrypt
|
2015-11-05 06:59:46 +00:00
|
|
|
m_cipher->ProcessBlock(m_register, m_temp);
|
|
|
|
unsigned int updateSize = BlockSize()-m_feedbackSize;
|
|
|
|
memmove_s(m_register, m_register.size(), m_register+m_feedbackSize, updateSize);
|
|
|
|
memcpy_s(m_register+updateSize, m_register.size()-updateSize, m_temp, m_feedbackSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CFB_ModePolicy::CipherResynchronize(const byte *iv, size_t length)
|
|
|
|
{
|
2016-09-16 15:27:15 +00:00
|
|
|
CRYPTOPP_ASSERT(length == BlockSize());
|
Add variable block size support for block ciphers
This should lead the way for more modern block ciphers like Threefish and Kalyna. It tested well with both regular cipher modes (the mode has an instance of the cipher) and external cipher modes (the cipher and mode are distinct objects, and the mode holds a reference to the cipher).
We still have to work out the details of naming a cipher. For example, Kalyna with a 128-bit key can use a 128-bit or 256-bit block size. Kalyna-128 is not enough to describe the algorithm and locate it in the object registry. Kalyna-128-128 looks kind of weird; maybe Kalyna-128(128) or Kalyna-128(256) would be better.
Here are the initial test cases to verify functionality:
byte key[64] = {}, iv[32] = {};
ECB_Mode<Kalyna>::Encryption enc1;
enc1.SetKey(key, 16);
CBC_Mode<Kalyna>::Encryption enc2;
enc2.SetKeyWithIV(key, 16, iv);
AlgorithmParameters params = MakeParameters
(Name::BlockSize(), 32)
(Name::IV(), ConstByteArrayParameter(iv, 32));
CTR_Mode<Kalyna>::Encryption enc3;
enc3.SetKey(key, 16, params);
CBC_Mode<Kalyna>::Encryption enc4;
enc4.SetKey(key, 32, params);
Kalyna::Encryption enc5;
ECB_Mode_ExternalCipher::Encryption ecb(enc5);
ecb.SetKey(key, 16, params);
Kalyna::Encryption enc6;
ECB_Mode_ExternalCipher::Encryption cbc(enc6);
cbc.SetKey(key, 32, params);
2017-05-01 20:23:57 +00:00
|
|
|
CopyOrZero(m_register, m_register.size(), iv, length);
|
2015-11-05 06:59:46 +00:00
|
|
|
TransformRegister();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CFB_ModePolicy::SetFeedbackSize(unsigned int feedbackSize)
|
|
|
|
{
|
|
|
|
if (feedbackSize > BlockSize())
|
|
|
|
throw InvalidArgument("CFB_Mode: invalid feedback size");
|
|
|
|
m_feedbackSize = feedbackSize ? feedbackSize : BlockSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CFB_ModePolicy::ResizeBuffers()
|
|
|
|
{
|
|
|
|
CipherModeBase::ResizeBuffers();
|
|
|
|
m_temp.New(BlockSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
void OFB_ModePolicy::WriteKeystream(byte *keystreamBuffer, size_t iterationCount)
|
|
|
|
{
|
2017-09-05 20:28:00 +00:00
|
|
|
CRYPTOPP_ASSERT(m_cipher->IsForwardTransformation()); // OFB mode needs the "encrypt" direction of the underlying block cipher, even to decrypt
|
|
|
|
unsigned int s = BlockSize();
|
2015-11-05 06:59:46 +00:00
|
|
|
m_cipher->ProcessBlock(m_register, keystreamBuffer);
|
|
|
|
if (iterationCount > 1)
|
2017-03-01 11:10:06 +00:00
|
|
|
m_cipher->AdvancedProcessBlocks(keystreamBuffer, NULLPTR, keystreamBuffer+s, s*(iterationCount-1), 0);
|
2017-09-05 20:28:00 +00:00
|
|
|
memcpy(m_register, keystreamBuffer+s*(iterationCount-1), s);
|
2015-11-05 06:59:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void OFB_ModePolicy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length)
|
|
|
|
{
|
|
|
|
CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
|
2016-09-16 15:27:15 +00:00
|
|
|
CRYPTOPP_ASSERT(length == BlockSize());
|
2015-11-05 06:59:46 +00:00
|
|
|
|
Add variable block size support for block ciphers
This should lead the way for more modern block ciphers like Threefish and Kalyna. It tested well with both regular cipher modes (the mode has an instance of the cipher) and external cipher modes (the cipher and mode are distinct objects, and the mode holds a reference to the cipher).
We still have to work out the details of naming a cipher. For example, Kalyna with a 128-bit key can use a 128-bit or 256-bit block size. Kalyna-128 is not enough to describe the algorithm and locate it in the object registry. Kalyna-128-128 looks kind of weird; maybe Kalyna-128(128) or Kalyna-128(256) would be better.
Here are the initial test cases to verify functionality:
byte key[64] = {}, iv[32] = {};
ECB_Mode<Kalyna>::Encryption enc1;
enc1.SetKey(key, 16);
CBC_Mode<Kalyna>::Encryption enc2;
enc2.SetKeyWithIV(key, 16, iv);
AlgorithmParameters params = MakeParameters
(Name::BlockSize(), 32)
(Name::IV(), ConstByteArrayParameter(iv, 32));
CTR_Mode<Kalyna>::Encryption enc3;
enc3.SetKey(key, 16, params);
CBC_Mode<Kalyna>::Encryption enc4;
enc4.SetKey(key, 32, params);
Kalyna::Encryption enc5;
ECB_Mode_ExternalCipher::Encryption ecb(enc5);
ecb.SetKey(key, 16, params);
Kalyna::Encryption enc6;
ECB_Mode_ExternalCipher::Encryption cbc(enc6);
cbc.SetKey(key, 32, params);
2017-05-01 20:23:57 +00:00
|
|
|
CopyOrZero(m_register, m_register.size(), iv, length);
|
2015-11-05 06:59:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CTR_ModePolicy::SeekToIteration(lword iterationCount)
|
|
|
|
{
|
|
|
|
int carry=0;
|
|
|
|
for (int i=BlockSize()-1; i>=0; i--)
|
|
|
|
{
|
|
|
|
unsigned int sum = m_register[i] + byte(iterationCount) + carry;
|
|
|
|
m_counterArray[i] = (byte) sum;
|
|
|
|
carry = sum >> 8;
|
|
|
|
iterationCount >>= 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTR_ModePolicy::IncrementCounterBy256()
|
|
|
|
{
|
|
|
|
IncrementCounterByOne(m_counterArray, BlockSize()-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTR_ModePolicy::OperateKeystream(KeystreamOperation /*operation*/, byte *output, const byte *input, size_t iterationCount)
|
|
|
|
{
|
2017-09-05 20:28:00 +00:00
|
|
|
CRYPTOPP_ASSERT(m_cipher->IsForwardTransformation()); // CTR mode needs the "encrypt" direction of the underlying block cipher, even to decrypt
|
|
|
|
unsigned int s = BlockSize();
|
|
|
|
unsigned int inputIncrement = input ? s : 0;
|
2015-11-05 06:59:46 +00:00
|
|
|
|
|
|
|
while (iterationCount)
|
|
|
|
{
|
|
|
|
byte lsb = m_counterArray[s-1];
|
2017-09-05 20:28:00 +00:00
|
|
|
size_t blocks = UnsignedMin(iterationCount, 256U-lsb);
|
|
|
|
m_cipher->AdvancedProcessBlocks(m_counterArray, input, output, blocks*s, BlockTransformation::BT_InBlockIsCounter|BlockTransformation::BT_AllowParallel);
|
2015-11-05 06:59:46 +00:00
|
|
|
if ((m_counterArray[s-1] = lsb + (byte)blocks) == 0)
|
|
|
|
IncrementCounterBy256();
|
|
|
|
|
|
|
|
output += blocks*s;
|
|
|
|
input += blocks*inputIncrement;
|
|
|
|
iterationCount -= blocks;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTR_ModePolicy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length)
|
|
|
|
{
|
|
|
|
CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
|
2016-09-16 15:27:15 +00:00
|
|
|
CRYPTOPP_ASSERT(length == BlockSize());
|
2015-11-05 06:59:46 +00:00
|
|
|
|
Add variable block size support for block ciphers
This should lead the way for more modern block ciphers like Threefish and Kalyna. It tested well with both regular cipher modes (the mode has an instance of the cipher) and external cipher modes (the cipher and mode are distinct objects, and the mode holds a reference to the cipher).
We still have to work out the details of naming a cipher. For example, Kalyna with a 128-bit key can use a 128-bit or 256-bit block size. Kalyna-128 is not enough to describe the algorithm and locate it in the object registry. Kalyna-128-128 looks kind of weird; maybe Kalyna-128(128) or Kalyna-128(256) would be better.
Here are the initial test cases to verify functionality:
byte key[64] = {}, iv[32] = {};
ECB_Mode<Kalyna>::Encryption enc1;
enc1.SetKey(key, 16);
CBC_Mode<Kalyna>::Encryption enc2;
enc2.SetKeyWithIV(key, 16, iv);
AlgorithmParameters params = MakeParameters
(Name::BlockSize(), 32)
(Name::IV(), ConstByteArrayParameter(iv, 32));
CTR_Mode<Kalyna>::Encryption enc3;
enc3.SetKey(key, 16, params);
CBC_Mode<Kalyna>::Encryption enc4;
enc4.SetKey(key, 32, params);
Kalyna::Encryption enc5;
ECB_Mode_ExternalCipher::Encryption ecb(enc5);
ecb.SetKey(key, 16, params);
Kalyna::Encryption enc6;
ECB_Mode_ExternalCipher::Encryption cbc(enc6);
cbc.SetKey(key, 32, params);
2017-05-01 20:23:57 +00:00
|
|
|
CopyOrZero(m_register, m_register.size(), iv, length);
|
2015-11-05 06:59:46 +00:00
|
|
|
m_counterArray = m_register;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BlockOrientedCipherModeBase::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms)
|
|
|
|
{
|
|
|
|
m_cipher->SetKey(key, length, params);
|
|
|
|
ResizeBuffers();
|
|
|
|
if (IsResynchronizable())
|
|
|
|
{
|
|
|
|
size_t ivLength;
|
|
|
|
const byte *iv = GetIVAndThrowIfInvalid(params, ivLength);
|
|
|
|
Resynchronize(iv, (int)ivLength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BlockOrientedCipherModeBase::ResizeBuffers()
|
|
|
|
{
|
|
|
|
CipherModeBase::ResizeBuffers();
|
|
|
|
m_buffer.New(BlockSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
void ECB_OneWay::ProcessData(byte *outString, const byte *inString, size_t length)
|
|
|
|
{
|
2016-09-16 15:27:15 +00:00
|
|
|
CRYPTOPP_ASSERT(length%BlockSize()==0);
|
2017-09-05 20:28:00 +00:00
|
|
|
m_cipher->AdvancedProcessBlocks(inString, NULLPTR, outString, length, BlockTransformation::BT_AllowParallel);
|
2015-11-05 06:59:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CBC_Encryption::ProcessData(byte *outString, const byte *inString, size_t length)
|
|
|
|
{
|
|
|
|
if (!length)
|
|
|
|
return;
|
2017-09-05 20:28:00 +00:00
|
|
|
CRYPTOPP_ASSERT(length%BlockSize()==0);
|
2015-11-05 06:59:46 +00:00
|
|
|
|
2017-09-05 20:28:00 +00:00
|
|
|
unsigned int blockSize = BlockSize();
|
2017-09-05 00:36:08 +00:00
|
|
|
m_cipher->AdvancedProcessBlocks(inString, m_register, outString, blockSize, BlockTransformation::BT_XorInput);
|
|
|
|
if (length > blockSize)
|
|
|
|
m_cipher->AdvancedProcessBlocks(inString+blockSize, outString, outString+blockSize, length-blockSize, BlockTransformation::BT_XorInput);
|
2017-09-05 20:28:00 +00:00
|
|
|
memcpy(m_register, outString + length - blockSize, blockSize);
|
2015-11-05 06:59:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CBC_CTS_Encryption::ProcessLastBlock(byte *outString, const byte *inString, size_t length)
|
|
|
|
{
|
|
|
|
if (length <= BlockSize())
|
|
|
|
{
|
|
|
|
if (!m_stolenIV)
|
|
|
|
throw InvalidArgument("CBC_Encryption: message is too short for ciphertext stealing");
|
|
|
|
|
|
|
|
// steal from IV
|
2017-09-05 20:28:00 +00:00
|
|
|
memcpy(outString, m_register, length);
|
2015-11-05 06:59:46 +00:00
|
|
|
outString = m_stolenIV;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// steal from next to last block
|
|
|
|
xorbuf(m_register, inString, BlockSize());
|
|
|
|
m_cipher->ProcessBlock(m_register);
|
|
|
|
inString += BlockSize();
|
|
|
|
length -= BlockSize();
|
2017-09-05 20:28:00 +00:00
|
|
|
memcpy(outString+BlockSize(), m_register, length);
|
2015-11-05 06:59:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// output last full ciphertext block
|
|
|
|
xorbuf(m_register, inString, length);
|
|
|
|
m_cipher->ProcessBlock(m_register);
|
2017-09-05 20:28:00 +00:00
|
|
|
memcpy(outString, m_register, BlockSize());
|
2015-11-05 06:59:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CBC_Decryption::ResizeBuffers()
|
|
|
|
{
|
|
|
|
BlockOrientedCipherModeBase::ResizeBuffers();
|
|
|
|
m_temp.New(BlockSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBC_Decryption::ProcessData(byte *outString, const byte *inString, size_t length)
|
|
|
|
{
|
|
|
|
if (!length)
|
|
|
|
return;
|
2017-09-05 20:28:00 +00:00
|
|
|
CRYPTOPP_ASSERT(length%BlockSize()==0);
|
2015-11-05 06:59:46 +00:00
|
|
|
|
2017-09-05 20:28:00 +00:00
|
|
|
unsigned int blockSize = BlockSize();
|
|
|
|
memcpy(m_temp, inString+length-blockSize, blockSize); // save copy now in case of in-place decryption
|
|
|
|
if (length > blockSize)
|
|
|
|
m_cipher->AdvancedProcessBlocks(inString+blockSize, inString, outString+blockSize, length-blockSize, BlockTransformation::BT_ReverseDirection|BlockTransformation::BT_AllowParallel);
|
|
|
|
m_cipher->ProcessAndXorBlock(inString, m_register, outString);
|
|
|
|
m_register.swap(m_temp);
|
2015-11-05 06:59:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CBC_CTS_Decryption::ProcessLastBlock(byte *outString, const byte *inString, size_t length)
|
|
|
|
{
|
|
|
|
const byte *pn, *pn1;
|
|
|
|
bool stealIV = length <= BlockSize();
|
|
|
|
|
|
|
|
if (stealIV)
|
|
|
|
{
|
|
|
|
pn = inString;
|
|
|
|
pn1 = m_register;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pn = inString + BlockSize();
|
|
|
|
pn1 = inString;
|
|
|
|
length -= BlockSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
// decrypt last partial plaintext block
|
2017-09-05 20:28:00 +00:00
|
|
|
memcpy(m_temp, pn1, BlockSize());
|
2015-11-05 06:59:46 +00:00
|
|
|
m_cipher->ProcessBlock(m_temp);
|
|
|
|
xorbuf(m_temp, pn, length);
|
|
|
|
|
|
|
|
if (stealIV)
|
2017-09-05 20:28:00 +00:00
|
|
|
memcpy(outString, m_temp, length);
|
2015-11-05 06:59:46 +00:00
|
|
|
else
|
|
|
|
{
|
2017-09-05 20:28:00 +00:00
|
|
|
memcpy(outString+BlockSize(), m_temp, length);
|
2015-11-05 06:59:46 +00:00
|
|
|
// decrypt next to last plaintext block
|
2017-09-05 20:28:00 +00:00
|
|
|
memcpy(m_temp, pn, length);
|
2015-11-05 06:59:46 +00:00
|
|
|
m_cipher->ProcessBlock(m_temp);
|
|
|
|
xorbuf(outString, m_temp, m_register, BlockSize());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NAMESPACE_END
|
|
|
|
|
|
|
|
#endif
|