Crypto++ 8.6 Free C++ class library of cryptographic schemes

strciphr.cpp
1 // strciphr.cpp - originally written and placed in the public domain by Wei Dai
2
3 // TODO: Figure out what is happening in ProcessData. The issue surfaced for
4 // CFB_CipherTemplate<BASE>::ProcessData when we cut-in Cryptogams
5 // AES ARMv7 asm. Then again in AdditiveCipherTemplate<S>::ProcessData
6 // for CTR mode with HIGHT, which is a 64-bit block cipher. In both cases,
7 // inString == outString leads to incorrect results. We think it relates to
8 // aliasing violations because inString == outString.
9 //
10 // Also see https://github.com/weidai11/cryptopp/issues/683 and
11 // https://github.com/weidai11/cryptopp/issues/1010.
12
13 #include "pch.h"
14
15 #ifndef CRYPTOPP_IMPORTS
16
17 #include "strciphr.h"
18
19 // Squash MS LNK4221 and libtool warnings
20 #ifndef CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES
21 extern const char STRCIPHER_FNAME[] = __FILE__;
22 #endif
23
24 NAMESPACE_BEGIN(CryptoPP)
25
26 template <class S>
27 void AdditiveCipherTemplate<S>::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs &params)
28 {
29 PolicyInterface &policy = this->AccessPolicy();
30 policy.CipherSetKey(params, key, length);
31 m_leftOver = 0;
32 unsigned int bufferByteSize = policy.CanOperateKeystream() ? GetBufferByteSize(policy) : RoundUpToMultipleOf(1024U, GetBufferByteSize(policy));
33 m_buffer.New(bufferByteSize);
34
35 if (this->IsResynchronizable())
36 {
37 size_t ivLength;
38 const byte *iv = this->GetIVAndThrowIfInvalid(params, ivLength);
39 policy.CipherResynchronize(m_buffer, iv, ivLength);
40 }
41 }
42
43 template <class S>
44 void AdditiveCipherTemplate<S>::GenerateBlock(byte *outString, size_t length)
45 {
46 if (m_leftOver > 0)
47 {
48 const size_t len = STDMIN(m_leftOver, length);
49 std::memcpy(outString, PtrSub(KeystreamBufferEnd(), m_leftOver), len);
50
51 length -= len; m_leftOver -= len;
52 outString = PtrAdd(outString, len);
53 if (!length) {return;}
54 }
55
56 PolicyInterface &policy = this->AccessPolicy();
57 unsigned int bytesPerIteration = policy.GetBytesPerIteration();
58
59 if (length >= bytesPerIteration)
60 {
61 const size_t iterations = length / bytesPerIteration;
62 policy.WriteKeystream(outString, iterations);
63 length -= iterations * bytesPerIteration;
64 outString = PtrAdd(outString, iterations * bytesPerIteration);
65 }
66
67 if (length > 0)
68 {
69 size_t bufferByteSize = RoundUpToMultipleOf(length, bytesPerIteration);
70 size_t bufferIterations = bufferByteSize / bytesPerIteration;
71
72 policy.WriteKeystream(PtrSub(KeystreamBufferEnd(), bufferByteSize), bufferIterations);
73 std::memcpy(outString, PtrSub(KeystreamBufferEnd(), bufferByteSize), length);
74 m_leftOver = bufferByteSize - length;
75 }
76 }
77
78 template <class S>
79 void AdditiveCipherTemplate<S>::ProcessData(byte *outString, const byte *inString, size_t length)
80 {
81 CRYPTOPP_ASSERT(outString); CRYPTOPP_ASSERT(inString);
82 CRYPTOPP_ASSERT(length % this->MandatoryBlockSize() == 0);
83
84 PolicyInterface &policy = this->AccessPolicy();
85 unsigned int bytesPerIteration = policy.GetBytesPerIteration();
86
87 // GCC and Clang do not like this for CTR mode and 64-bit ciphers like HIGHT.
88 // The incorrect result is a partial string of 0's instead of plaintext or
89 // ciphertext. Recovered plaintext is partially garbage.
90 //
91 // It almost feels as if the compiler does not see the string is transformed
92 // in-place so it short-circuits the transform. In this case, if we use a
93 // stand-alone reproducer with the same data then the issue is present.
94
95 byte* savedOutString = outString;
96 size_t savedLength = length;
97 bool copyOut = false;
98
99 if (inString == outString)
100 {
101 // No need to copy inString to outString.
102 // Just allocate the space.
103 m_tempOutString.New(length);
104 m_tempOutString.SetMark(0);
105 outString = m_tempOutString.BytePtr();
106 copyOut = true;
107 }
108
109 if (m_leftOver > 0)
110 {
111 const size_t len = STDMIN(m_leftOver, length);
112 xorbuf(outString, inString, PtrSub(KeystreamBufferEnd(), m_leftOver), len);
113
114 inString = PtrAdd(inString, len);
115 outString = PtrAdd(outString, len);
116 length -= len; m_leftOver -= len;
117 }
118
119 if (!length) {
120 if (copyOut)
121 std::memcpy(savedOutString, m_tempOutString.BytePtr(), savedLength);
122 return;
123 }
124
125 const unsigned int alignment = policy.GetAlignment();
126 const bool inAligned = IsAlignedOn(inString, alignment);
127 const bool outAligned = IsAlignedOn(outString, alignment);
128 CRYPTOPP_UNUSED(inAligned); CRYPTOPP_UNUSED(outAligned);
129
130 if (policy.CanOperateKeystream() && length >= bytesPerIteration)
131 {
132 const size_t iterations = length / bytesPerIteration;
133 KeystreamOperationFlags flags = static_cast<KeystreamOperationFlags>(
134 (inAligned ? EnumToInt(INPUT_ALIGNED) : 0) | (outAligned ? EnumToInt(OUTPUT_ALIGNED) : 0));
135 KeystreamOperation operation = KeystreamOperation(flags);
136 policy.OperateKeystream(operation, outString, inString, iterations);
137
138 inString = PtrAdd(inString, iterations * bytesPerIteration);
139 outString = PtrAdd(outString, iterations * bytesPerIteration);
140 length -= iterations * bytesPerIteration;
141 }
142
143 size_t bufferByteSize = m_buffer.size();
144 size_t bufferIterations = bufferByteSize / bytesPerIteration;
145
146 while (length >= bufferByteSize)
147 {
148 policy.WriteKeystream(m_buffer, bufferIterations);
149 xorbuf(outString, inString, KeystreamBufferBegin(), bufferByteSize);
150
151 inString = PtrAdd(inString, bufferByteSize);
152 outString = PtrAdd(outString, bufferByteSize);
153 length -= bufferByteSize;
154 }
155
156 if (length > 0)
157 {
158 bufferByteSize = RoundUpToMultipleOf(length, bytesPerIteration);
159 bufferIterations = bufferByteSize / bytesPerIteration;
160
161 policy.WriteKeystream(PtrSub(KeystreamBufferEnd(), bufferByteSize), bufferIterations);
162 xorbuf(outString, inString, PtrSub(KeystreamBufferEnd(), bufferByteSize), length);
163
164 m_leftOver = bufferByteSize - length;
165 }
166
167 if (copyOut)
168 std::memcpy(savedOutString, m_tempOutString.BytePtr(), savedLength);
169 }
170
171 template <class S>
172 void AdditiveCipherTemplate<S>::Resynchronize(const byte *iv, int length)
173 {
174 PolicyInterface &policy = this->AccessPolicy();
175 m_leftOver = 0;
176 m_buffer.New(GetBufferByteSize(policy));
177 policy.CipherResynchronize(m_buffer, iv, this->ThrowIfInvalidIVLength(length));
178 }
179
180 template <class BASE>
181 void AdditiveCipherTemplate<BASE>::Seek(lword position)
182 {
183 PolicyInterface &policy = this->AccessPolicy();
184 unsigned int bytesPerIteration = policy.GetBytesPerIteration();
185
186 policy.SeekToIteration(position / bytesPerIteration);
187 position %= bytesPerIteration;
188
189 if (position > 0)
190 {
191 policy.WriteKeystream(PtrSub(KeystreamBufferEnd(), bytesPerIteration), 1);
192 m_leftOver = bytesPerIteration - static_cast<unsigned int>(position);
193 }
194 else
195 m_leftOver = 0;
196 }
197
198 template <class BASE>
199 void CFB_CipherTemplate<BASE>::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs &params)
200 {
201 PolicyInterface &policy = this->AccessPolicy();
202 policy.CipherSetKey(params, key, length);
203
204 if (this->IsResynchronizable())
205 {
206 size_t ivLength;
207 const byte *iv = this->GetIVAndThrowIfInvalid(params, ivLength);
208 policy.CipherResynchronize(iv, ivLength);
209 }
210
211 m_leftOver = policy.GetBytesPerIteration();
212 }
213
214 template <class BASE>
215 void CFB_CipherTemplate<BASE>::Resynchronize(const byte *iv, int length)
216 {
217 PolicyInterface &policy = this->AccessPolicy();
218 policy.CipherResynchronize(iv, this->ThrowIfInvalidIVLength(length));
219 m_leftOver = policy.GetBytesPerIteration();
220 }
221
222 template <class BASE>
223 void CFB_CipherTemplate<BASE>::ProcessData(byte *outString, const byte *inString, size_t length)
224 {
225 CRYPTOPP_ASSERT(outString); CRYPTOPP_ASSERT(inString);
226 CRYPTOPP_ASSERT(length % this->MandatoryBlockSize() == 0);
227
228 PolicyInterface &policy = this->AccessPolicy();
229 unsigned int bytesPerIteration = policy.GetBytesPerIteration();
230 byte *reg = policy.GetRegisterBegin();
231
232 // GCC and Clang do not like this on ARM when inString == outString. The incorrect
233 // result is a string of 0's instead of plaintext or ciphertext. The 0's trace back
234 // to the allocation for the std::string in datatest.cpp. Elements in the string
235 // are initialized to their default value, which is 0.
236 //
237 // It almost feels as if the compiler does not see the string is transformed
238 // in-place so it short-circuits the transform. However, if we use a stand-alone
239 // reproducer with the same data then the issue is _not_ present.
240
241 byte* savedOutString = outString;
242 size_t savedLength = length;
243 bool copyOut = false;
244
245 if (inString == outString)
246 {
247 // No need to copy inString to outString.
248 // Just allocate the space.
249 m_tempOutString.New(length);
250 m_tempOutString.SetMark(0);
251 outString = m_tempOutString.BytePtr();
252 copyOut = true;
253 }
254
255 if (m_leftOver)
256 {
257 const size_t len = STDMIN(m_leftOver, length);
258 CombineMessageAndShiftRegister(outString, PtrAdd(reg, bytesPerIteration - m_leftOver), inString, len);
259
260 inString = PtrAdd(inString, len);
261 outString = PtrAdd(outString, len);
262 m_leftOver -= len; length -= len;
263 }
264
265 if (!length) {
266 if (copyOut)
267 std::memcpy(savedOutString, m_tempOutString.BytePtr(), savedLength);
268 return;
269 }
270
271 const unsigned int alignment = policy.GetAlignment();
272 const bool inAligned = IsAlignedOn(inString, alignment);
273 const bool outAligned = IsAlignedOn(outString, alignment);
274 CRYPTOPP_UNUSED(inAligned); CRYPTOPP_UNUSED(outAligned);
275
276 if (policy.CanIterate() && length >= bytesPerIteration && outAligned)
277 {
278 CipherDir cipherDir = GetCipherDir(*this);
279 policy.Iterate(outString, inString, cipherDir, length / bytesPerIteration);
280
281 const size_t remainder = length % bytesPerIteration;
282 inString = PtrAdd(inString, length - remainder);
283 outString = PtrAdd(outString, length - remainder);
284 length = remainder;
285 }
286
287 while (length >= bytesPerIteration)
288 {
289 policy.TransformRegister();
290 CombineMessageAndShiftRegister(outString, reg, inString, bytesPerIteration);
291
292 inString = PtrAdd(inString, bytesPerIteration);
293 outString = PtrAdd(outString, bytesPerIteration);
294 length -= bytesPerIteration;
295 }
296
297 if (length > 0)
298 {
299 policy.TransformRegister();
300 CombineMessageAndShiftRegister(outString, reg, inString, length);
301 m_leftOver = bytesPerIteration - length;
302 }
303
304 if (copyOut)
305 std::memcpy(savedOutString, m_tempOutString.BytePtr(), savedLength);
306 }
307
308 template <class BASE>
309 void CFB_EncryptionTemplate<BASE>::CombineMessageAndShiftRegister(byte *output, byte *reg, const byte *message, size_t length)
310 {
311 xorbuf(reg, message, length);
312 std::memcpy(output, reg, length);
313 }
314
315 template <class BASE>
316 void CFB_DecryptionTemplate<BASE>::CombineMessageAndShiftRegister(byte *output, byte *reg, const byte *message, size_t length)
317 {
318 for (size_t i=0; i<length; i++)
319 {
320 byte b = message[i];
321 output[i] = reg[i] ^ b;
322 reg[i] = b;
323 }
324 }
325
326 NAMESPACE_END
327
328 #endif
IsAlignedOn
bool IsAlignedOn(const void *ptr, unsigned int alignment)
Determines whether ptr is aligned to a minimum value.
Definition: misc.h:1226
RoundUpToMultipleOf
T1 RoundUpToMultipleOf(const T1 &n, const T2 &m)
Rounds a value up to a multiple of a second value.
Definition: misc.h:1174
INPUT_ALIGNED
@ INPUT_ALIGNED
Input buffer is aligned.
Definition: strciphr.h:80
CRYPTOPP_ASSERT
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition: trap.h:68
GetCipherDir
CipherDir GetCipherDir(const T &obj)
Returns the direction the cipher is being operated.
Definition: misc.h:1287
pch.h
Precompiled header file.
CFB_CipherTemplate::Resynchronize
void Resynchronize(const byte *iv, int length=-1)
Resynchronize the cipher.
CipherDir
CipherDir
Specifies a direction for a cipher to operate.
Definition: cryptlib.h:123
PtrSub
PTR PtrSub(PTR pointer, OFF offset)
Create a pointer with an offset.
Definition: misc.h:399
STDMIN
const T & STDMIN(const T &a, const T &b)
Replacement function for std::min.
Definition: misc.h:655
AdditiveCipherTemplate
Base class for additive stream ciphers with SymmetricCipher interface.
Definition: strciphr.h:297
KeystreamOperation
KeystreamOperation
Keystream operation flags.
Definition: strciphr.h:88
AdditiveCipherTemplate::Seek
void Seek(lword position)
Seeks to a random position in the stream.
AdditiveCipherTemplate::GenerateBlock
void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
CFB_CipherTemplate::ProcessData
void ProcessData(byte *outString, const byte *inString, size_t length)
Apply keystream to data.
CFB_CipherTemplate
Base class for feedback based stream ciphers with SymmetricCipher interface.
Definition: strciphr.h:562
lword
word64 lword
Large word type.
Definition: config_int.h:158
CFB_DecryptionTemplate
Base class for feedback based stream ciphers in the reverse direction with SymmetricCipher interface.
Definition: strciphr.h:654
OUTPUT_ALIGNED
@ OUTPUT_ALIGNED
Output buffer is aligned.
Definition: strciphr.h:78
KeystreamOperationFlags
KeystreamOperationFlags
Keystream operation flags.
Definition: strciphr.h:76
CryptoPP
Crypto++ library namespace.
AdditiveCipherTemplate::Resynchronize
void Resynchronize(const byte *iv, int length=-1)
Resynchronize the cipher.
EnumToInt
#define EnumToInt(v)
Integer value.
Definition: misc.h:502
xorbuf
CRYPTOPP_DLL void xorbuf(byte *buf, const byte *mask, size_t count)
Performs an XOR of a buffer with a mask.
PtrAdd
PTR PtrAdd(PTR pointer, OFF offset)
Create a pointer with an offset.
Definition: misc.h:386
CFB_EncryptionTemplate
Base class for feedback based stream ciphers in the forward direction with SymmetricCipher interface.
Definition: strciphr.h:645
AdditiveCipherTemplate::ProcessData
void ProcessData(byte *outString, const byte *inString, size_t length)
Apply keystream to data.
NameValuePairs
Interface for retrieving values given their names.
Definition: cryptlib.h:321
strciphr.h
Classes for implementing stream ciphers.