1 // modes.cpp - originally written and placed in the public domain by Wei Dai
2 
3 #include "pch.h"
4 
5 #ifndef CRYPTOPP_IMPORTS
6 
7 #include "modes.h"
8 #include "misc.h"
9 
10 #if defined(CRYPTOPP_DEBUG)
11 #include "des.h"
12 #endif
13 
NAMESPACE_BEGIN(CryptoPP)14 NAMESPACE_BEGIN(CryptoPP)
15 
16 #if defined(CRYPTOPP_DEBUG) && !defined(CRYPTOPP_DOXYGEN_PROCESSING)
17 void Modes_TestInstantiations()
18 {
19 	CFB_Mode<DES>::Encryption m0;
20 	CFB_Mode<DES>::Decryption m1;
21 	OFB_Mode<DES>::Encryption m2;
22 	CTR_Mode<DES>::Encryption m3;
23 	ECB_Mode<DES>::Encryption m4;
24 	CBC_Mode<DES>::Encryption m5;
25 }
26 #endif
27 
ResizeBuffers()28 void CipherModeBase::ResizeBuffers()
29 {
30 	m_register.New(m_cipher->BlockSize());
31 }
32 
Iterate(byte * output,const byte * input,CipherDir dir,size_t iterationCount)33 void CFB_ModePolicy::Iterate(byte *output, const byte *input, CipherDir dir, size_t iterationCount)
34 {
35 	CRYPTOPP_ASSERT(input);	CRYPTOPP_ASSERT(output);
36 	CRYPTOPP_ASSERT(m_cipher->IsForwardTransformation());
37 	CRYPTOPP_ASSERT(m_register.size() == BlockSize());
38 	CRYPTOPP_ASSERT(m_temp.size() == BlockSize());
39 	CRYPTOPP_ASSERT(iterationCount > 0);
40 
41 	const unsigned int s = BlockSize();
42 	if (dir == ENCRYPTION)
43 	{
44 		m_cipher->ProcessAndXorBlock(m_register, input, output);
45 		if (iterationCount > 1)
46 			m_cipher->AdvancedProcessBlocks(output, PtrAdd(input,s), PtrAdd(output,s), (iterationCount-1)*s, 0);
47 		memcpy(m_register, PtrAdd(output,(iterationCount-1)*s), s);
48 	}
49 	else
50 	{
51 		// make copy first in case of in-place decryption
52 		memcpy(m_temp, PtrAdd(input,(iterationCount-1)*s), s);
53 		if (iterationCount > 1)
54 			m_cipher->AdvancedProcessBlocks(input, PtrAdd(input,s), PtrAdd(output,s), (iterationCount-1)*s, BlockTransformation::BT_ReverseDirection);
55 		m_cipher->ProcessAndXorBlock(m_register, input, output);
56 		memcpy(m_register, m_temp, s);
57 	}
58 }
59 
TransformRegister()60 void CFB_ModePolicy::TransformRegister()
61 {
62 	CRYPTOPP_ASSERT(m_cipher->IsForwardTransformation());
63 	CRYPTOPP_ASSERT(m_register.size() == BlockSize());
64 	CRYPTOPP_ASSERT(m_temp.size() == BlockSize());
65 
66 	const ptrdiff_t updateSize = BlockSize()-m_feedbackSize;
67 	m_cipher->ProcessBlock(m_register, m_temp);
68 	memmove_s(m_register, m_register.size(), PtrAdd(m_register.begin(),m_feedbackSize), updateSize);
69 	memcpy_s(PtrAdd(m_register.begin(),updateSize), m_register.size()-updateSize, m_temp, m_feedbackSize);
70 }
71 
CipherResynchronize(const byte * iv,size_t length)72 void CFB_ModePolicy::CipherResynchronize(const byte *iv, size_t length)
73 {
74 	CRYPTOPP_ASSERT(length == BlockSize());
75 	CRYPTOPP_ASSERT(m_register.size() == BlockSize());
76 
77 	CopyOrZero(m_register, m_register.size(), iv, length);
78 	TransformRegister();
79 }
80 
SetFeedbackSize(unsigned int feedbackSize)81 void CFB_ModePolicy::SetFeedbackSize(unsigned int feedbackSize)
82 {
83 	if (feedbackSize > BlockSize())
84 		throw InvalidArgument("CFB_Mode: invalid feedback size");
85 	m_feedbackSize = feedbackSize ? feedbackSize : BlockSize();
86 }
87 
ResizeBuffers()88 void CFB_ModePolicy::ResizeBuffers()
89 {
90 	CipherModeBase::ResizeBuffers();
91 	m_temp.New(BlockSize());
92 }
93 
GetRegisterBegin()94 byte* CFB_ModePolicy::GetRegisterBegin()
95 {
96 	CRYPTOPP_ASSERT(!m_register.empty());
97 	CRYPTOPP_ASSERT(BlockSize() >= m_feedbackSize);
98 	return PtrAdd(m_register.begin(), BlockSize() - m_feedbackSize);
99 }
100 
WriteKeystream(byte * keystreamBuffer,size_t iterationCount)101 void OFB_ModePolicy::WriteKeystream(byte *keystreamBuffer, size_t iterationCount)
102 {
103 	CRYPTOPP_ASSERT(m_cipher->IsForwardTransformation());
104 	CRYPTOPP_ASSERT(m_register.size() == BlockSize());
105 	CRYPTOPP_ASSERT(iterationCount > 0);
106 
107 	const unsigned int s = BlockSize();
108 	m_cipher->ProcessBlock(m_register, keystreamBuffer);
109 	if (iterationCount > 1)
110 		m_cipher->AdvancedProcessBlocks(keystreamBuffer, NULLPTR, PtrAdd(keystreamBuffer, s), s*(iterationCount-1), 0);
111 	memcpy(m_register, PtrAdd(keystreamBuffer, (iterationCount-1)*s), s);
112 }
113 
CipherResynchronize(byte * keystreamBuffer,const byte * iv,size_t length)114 void OFB_ModePolicy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length)
115 {
116 	CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
117 	CRYPTOPP_ASSERT(m_register.size() == BlockSize());
118 	CRYPTOPP_ASSERT(length == BlockSize());
119 
120 	CopyOrZero(m_register, m_register.size(), iv, length);
121 }
122 
SeekToIteration(lword iterationCount)123 void CTR_ModePolicy::SeekToIteration(lword iterationCount)
124 {
125 	int carry=0;
126 	for (int i=BlockSize()-1; i>=0; i--)
127 	{
128 		unsigned int sum = m_register[i] + (byte)iterationCount + carry;
129 		m_counterArray[i] = byte(sum & 0xff);
130 		carry = sum >> 8;
131 		iterationCount >>= 8;
132 	}
133 }
134 
IncrementCounterBy256()135 void CTR_ModePolicy::IncrementCounterBy256()
136 {
137 	IncrementCounterByOne(m_counterArray, BlockSize()-1);
138 }
139 
OperateKeystream(KeystreamOperation,byte * output,const byte * input,size_t iterationCount)140 void CTR_ModePolicy::OperateKeystream(KeystreamOperation /*operation*/, byte *output, const byte *input, size_t iterationCount)
141 {
142 	CRYPTOPP_ASSERT(m_cipher->IsForwardTransformation());
143 	CRYPTOPP_ASSERT(m_counterArray.size() == BlockSize());
144 
145 	const unsigned int s = BlockSize();
146 	const unsigned int inputIncrement = input ? s : 0;
147 
148 	while (iterationCount)
149 	{
150 		const byte lsb = m_counterArray[s-1];
151 		const size_t blocks = UnsignedMin(iterationCount, 256U-lsb);
152 
153 		m_cipher->AdvancedProcessBlocks(m_counterArray, input, output, blocks*s, BlockTransformation::BT_InBlockIsCounter|BlockTransformation::BT_AllowParallel);
154 		if ((m_counterArray[s-1] = byte(lsb + blocks)) == 0)
155 			IncrementCounterBy256();
156 
157 		output = PtrAdd(output, blocks*s);
158 		input = PtrAdd(input, blocks*inputIncrement);
159 		iterationCount -= blocks;
160 	}
161 }
162 
CipherResynchronize(byte * keystreamBuffer,const byte * iv,size_t length)163 void CTR_ModePolicy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length)
164 {
165 	CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
166 	CRYPTOPP_ASSERT(m_register.size() == BlockSize());
167 	CRYPTOPP_ASSERT(length == BlockSize());
168 
169 	CopyOrZero(m_register, m_register.size(), iv, length);
170 	m_counterArray.Assign(m_register.begin(), m_register.size());
171 }
172 
UncheckedSetKey(const byte * key,unsigned int length,const NameValuePairs & params)173 void BlockOrientedCipherModeBase::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs &params)
174 {
175 	m_cipher->SetKey(key, length, params);
176 	ResizeBuffers();
177 	if (IsResynchronizable())
178 	{
179 		size_t ivLength;
180 		const byte *iv = GetIVAndThrowIfInvalid(params, ivLength);
181 		Resynchronize(iv, (int)ivLength);
182 	}
183 }
184 
ResizeBuffers()185 void BlockOrientedCipherModeBase::ResizeBuffers()
186 {
187 	CipherModeBase::ResizeBuffers();
188 	m_buffer.New(BlockSize());
189 }
190 
ProcessData(byte * outString,const byte * inString,size_t length)191 void ECB_OneWay::ProcessData(byte *outString, const byte *inString, size_t length)
192 {
193 	CRYPTOPP_ASSERT(length%BlockSize()==0);
194 	m_cipher->AdvancedProcessBlocks(inString, NULLPTR, outString, length, BlockTransformation::BT_AllowParallel);
195 }
196 
ProcessData(byte * outString,const byte * inString,size_t length)197 void CBC_Encryption::ProcessData(byte *outString, const byte *inString, size_t length)
198 {
199 	CRYPTOPP_ASSERT(length%BlockSize()==0);
200 	CRYPTOPP_ASSERT(m_register.size() == BlockSize());
201 	if (!length) return;
202 
203 	const unsigned int blockSize = BlockSize();
204 	m_cipher->AdvancedProcessBlocks(inString, m_register, outString, blockSize, BlockTransformation::BT_XorInput);
205 	if (length > blockSize)
206 		m_cipher->AdvancedProcessBlocks(PtrAdd(inString,blockSize), outString, PtrAdd(outString,blockSize), length-blockSize, BlockTransformation::BT_XorInput);
207 	memcpy(m_register, PtrAdd(outString, length - blockSize), blockSize);
208 }
209 
ProcessLastBlock(byte * outString,size_t outLength,const byte * inString,size_t inLength)210 size_t CBC_CTS_Encryption::ProcessLastBlock(byte *outString, size_t outLength, const byte *inString, size_t inLength)
211 {
212 	CRYPTOPP_UNUSED(outLength);
213 	const size_t used = inLength;
214 	const unsigned int blockSize = BlockSize();
215 
216 	if (inLength <= blockSize)
217 	{
218 		if (!m_stolenIV)
219 			throw InvalidArgument("CBC_Encryption: message is too short for ciphertext stealing");
220 
221 		// steal from IV
222 		memcpy(outString, m_register, inLength);
223 		outString = m_stolenIV;
224 	}
225 	else
226 	{
227 		// steal from next to last block
228 		xorbuf(m_register, inString, blockSize);
229 		m_cipher->ProcessBlock(m_register);
230 		inString = PtrAdd(inString, blockSize);
231 		inLength -= blockSize;
232 		memcpy(PtrAdd(outString, blockSize), m_register, inLength);
233 	}
234 
235 	// output last full ciphertext block
236 	xorbuf(m_register, inString, inLength);
237 	m_cipher->ProcessBlock(m_register);
238 	memcpy(outString, m_register, blockSize);
239 
240 	return used;
241 }
242 
ResizeBuffers()243 void CBC_Decryption::ResizeBuffers()
244 {
245 	BlockOrientedCipherModeBase::ResizeBuffers();
246 	m_temp.New(BlockSize());
247 }
248 
ProcessData(byte * outString,const byte * inString,size_t length)249 void CBC_Decryption::ProcessData(byte *outString, const byte *inString, size_t length)
250 {
251 	CRYPTOPP_ASSERT(length%BlockSize()==0);
252 	if (!length) {return;}
253 
254 	// save copy now in case of in-place decryption
255 	const unsigned int blockSize = BlockSize();
256 	memcpy(m_temp, PtrAdd(inString,length-blockSize), blockSize);
257 	if (length > blockSize)
258 		m_cipher->AdvancedProcessBlocks(PtrAdd(inString,blockSize), inString, PtrAdd(outString,blockSize), length-blockSize, BlockTransformation::BT_ReverseDirection|BlockTransformation::BT_AllowParallel);
259 	m_cipher->ProcessAndXorBlock(inString, m_register, outString);
260 	m_register.swap(m_temp);
261 }
262 
ProcessLastBlock(byte * outString,size_t outLength,const byte * inString,size_t inLength)263 size_t CBC_CTS_Decryption::ProcessLastBlock(byte *outString, size_t outLength, const byte *inString, size_t inLength)
264 {
265 	CRYPTOPP_UNUSED(outLength);
266 	const byte *pn1, *pn2;
267 	const size_t used = inLength;
268 	const bool stealIV = inLength <= BlockSize();
269 	const unsigned int blockSize = BlockSize();
270 
271 	if (stealIV)
272 	{
273 		pn1 = inString;
274 		pn2 = m_register;
275 	}
276 	else
277 	{
278 		pn1 = PtrAdd(inString, blockSize);
279 		pn2 = inString;
280 		inLength -= blockSize;
281 	}
282 
283 	// decrypt last partial plaintext block
284 	memcpy(m_temp, pn2, blockSize);
285 	m_cipher->ProcessBlock(m_temp);
286 	xorbuf(m_temp, pn1, inLength);
287 
288 	if (stealIV)
289 	{
290 		memcpy(outString, m_temp, inLength);
291 	}
292 	else
293 	{
294 		memcpy(PtrAdd(outString, blockSize), m_temp, inLength);
295 		// decrypt next to last plaintext block
296 		memcpy(m_temp, pn1, inLength);
297 		m_cipher->ProcessBlock(m_temp);
298 		xorbuf(outString, m_temp, m_register, blockSize);
299 	}
300 
301 	return used;
302 }
303 
304 NAMESPACE_END
305 
306 #endif
307