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;
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>
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:298
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:563
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:655
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:646
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:322
strciphr.h
Classes for implementing stream ciphers.