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
21extern const char STRCIPHER_FNAME[] = __FILE__;
22#endif
23
24NAMESPACE_BEGIN(CryptoPP)
25
26template <class S>
27void 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
43template <class S>
44void 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
78template <class S>
79void 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;
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
171template <class S>
172void 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
180template <class BASE>
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
198template <class BASE>
199void 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
214template <class BASE>
215void 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
222template <class BASE>
223void 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
308template <class BASE>
309void 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
315template <class BASE>
316void 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
326NAMESPACE_END
327
328#endif
Base class for additive stream ciphers with SymmetricCipher interface.
Definition: strciphr.h:298
void ProcessData(byte *outString, const byte *inString, size_t length)
Apply keystream to data.
void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
void Seek(lword position)
Seeks to a random position in the stream.
void Resynchronize(const byte *iv, int length=-1)
Resynchronize the cipher.
Base class for feedback based stream ciphers with SymmetricCipher interface.
Definition: strciphr.h:563
void Resynchronize(const byte *iv, int length=-1)
Resynchronize the cipher.
void ProcessData(byte *outString, const byte *inString, size_t length)
Apply keystream to data.
Base class for feedback based stream ciphers in the reverse direction with SymmetricCipher interface.
Definition: strciphr.h:655
Base class for feedback based stream ciphers in the forward direction with SymmetricCipher interface.
Definition: strciphr.h:646
Interface for retrieving values given their names.
Definition: cryptlib.h:322
word64 lword
Large word type.
Definition: config_int.h:158
CipherDir
Specifies a direction for a cipher to operate.
Definition: cryptlib.h:123
T1 RoundUpToMultipleOf(const T1 &n, const T2 &m)
Rounds a value up to a multiple of a second value.
Definition: misc.h:1174
PTR PtrSub(PTR pointer, OFF offset)
Create a pointer with an offset.
Definition: misc.h:399
const T & STDMIN(const T &a, const T &b)
Replacement function for std::min.
Definition: misc.h:655
bool IsAlignedOn(const void *ptr, unsigned int alignment)
Determines whether ptr is aligned to a minimum value.
Definition: misc.h:1226
PTR PtrAdd(PTR pointer, OFF offset)
Create a pointer with an offset.
Definition: misc.h:386
#define EnumToInt(v)
Integer value.
Definition: misc.h:502
CipherDir GetCipherDir(const T &obj)
Returns the direction the cipher is being operated.
Definition: misc.h:1287
CRYPTOPP_DLL void xorbuf(byte *buf, const byte *mask, size_t count)
Performs an XOR of a buffer with a mask.
Crypto++ library namespace.
Precompiled header file.
Classes for implementing stream ciphers.
KeystreamOperation
Keystream operation flags.
Definition: strciphr.h:88
KeystreamOperationFlags
Keystream operation flags.
Definition: strciphr.h:76
@ INPUT_ALIGNED
Input buffer is aligned.
Definition: strciphr.h:80
@ OUTPUT_ALIGNED
Output buffer is aligned.
Definition: strciphr.h:78
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition: trap.h:68