Crypto++  8.8
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
4 // for 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
8 // to aliasing violations because inString == outString.
9 //
10 // Also see https://github.com/weidai11/cryptopp/issues/683,
11 // https://github.com/weidai11/cryptopp/issues/1010 and
12 // https://github.com/weidai11/cryptopp/issues/1088.
13 
14 #include "pch.h"
15 
16 #ifndef CRYPTOPP_IMPORTS
17 
18 #include "strciphr.h"
19 
20 // Squash MS LNK4221 and libtool warnings
21 #ifndef CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES
22 extern const char STRCIPHER_FNAME[] = __FILE__;
23 #endif
24 
25 NAMESPACE_BEGIN(CryptoPP)
26 
27 template <class S>
28 void AdditiveCipherTemplate<S>::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs &params)
29 {
30  PolicyInterface &policy = this->AccessPolicy();
31  policy.CipherSetKey(params, key, length);
32  m_leftOver = 0;
33  unsigned int bufferByteSize = policy.CanOperateKeystream() ? GetBufferByteSize(policy) : RoundUpToMultipleOf(1024U, GetBufferByteSize(policy));
34  m_buffer.New(bufferByteSize);
35 
36  if (this->IsResynchronizable())
37  {
38  size_t ivLength;
39  const byte *iv = this->GetIVAndThrowIfInvalid(params, ivLength);
40  policy.CipherResynchronize(m_buffer, iv, ivLength);
41  }
42 }
43 
44 template <class S>
45 void AdditiveCipherTemplate<S>::GenerateBlock(byte *outString, size_t length)
46 {
47  if (m_leftOver > 0)
48  {
49  const size_t len = STDMIN(m_leftOver, length);
50  std::memcpy(outString, PtrSub(KeystreamBufferEnd(), m_leftOver), len);
51 
52  length -= len; m_leftOver -= len;
53  outString = PtrAdd(outString, len);
54  if (!length) {return;}
55  }
56 
57  PolicyInterface &policy = this->AccessPolicy();
58  size_t bytesPerIteration = policy.GetBytesPerIteration();
59 
60  if (length >= bytesPerIteration)
61  {
62  const size_t iterations = length / bytesPerIteration;
63  policy.WriteKeystream(outString, iterations);
64  length -= iterations * bytesPerIteration;
65  outString = PtrAdd(outString, iterations * bytesPerIteration);
66  }
67 
68  if (length > 0)
69  {
70  size_t bufferByteSize = RoundUpToMultipleOf(length, bytesPerIteration);
71  size_t bufferIterations = bufferByteSize / bytesPerIteration;
72 
73  policy.WriteKeystream(PtrSub(KeystreamBufferEnd(), bufferByteSize), bufferIterations);
74  std::memcpy(outString, PtrSub(KeystreamBufferEnd(), bufferByteSize), length);
75  m_leftOver = bufferByteSize - length;
76  }
77 }
78 
79 // TODO: Figure out what is happening in ProcessData. The issue surfaced
80 // for CFB_CipherTemplate<BASE>::ProcessData when we cut-in Cryptogams
81 // AES ARMv7 asm. Then again in AdditiveCipherTemplate<S>::ProcessData
82 // for CTR mode with HIGHT, which is a 64-bit block cipher. In both cases,
83 // inString == outString leads to incorrect results. We think it relates
84 // to aliasing violations because inString == outString.
85 //
86 // Also see https://github.com/weidai11/cryptopp/issues/683,
87 // https://github.com/weidai11/cryptopp/issues/1010 and
88 // https://github.com/weidai11/cryptopp/issues/1088.
89 
90 template <class S>
91 void AdditiveCipherTemplate<S>::ProcessData(byte *outString, const byte *inString, size_t length)
92 {
93  CRYPTOPP_ASSERT(outString); CRYPTOPP_ASSERT(inString);
94  CRYPTOPP_ASSERT(length % this->MandatoryBlockSize() == 0);
95 
96  PolicyInterface &policy = this->AccessPolicy();
97  size_t bytesPerIteration = policy.GetBytesPerIteration();
98 
99  if (m_leftOver > 0)
100  {
101  const size_t len = STDMIN(m_leftOver, length);
102  xorbuf(outString, inString, PtrSub(KeystreamBufferEnd(), m_leftOver), len);
103 
104  inString = PtrAdd(inString, len);
105  outString = PtrAdd(outString, len);
106  length -= len; m_leftOver -= len;
107  }
108 
109  if (!length) { return; }
110 
111  const word32 alignment = policy.GetAlignment();
112  const bool inAligned = IsAlignedOn(inString, alignment);
113  const bool outAligned = IsAlignedOn(outString, alignment);
114  CRYPTOPP_UNUSED(inAligned); CRYPTOPP_UNUSED(outAligned);
115 
116  if (policy.CanOperateKeystream() && length >= bytesPerIteration)
117  {
118  const size_t iterations = length / bytesPerIteration;
120  (inAligned ? EnumToInt(INPUT_ALIGNED) : 0) | (outAligned ? EnumToInt(OUTPUT_ALIGNED) : 0));
121  KeystreamOperation operation = KeystreamOperation(flags);
122  policy.OperateKeystream(operation, outString, inString, iterations);
123 
124  // Try to tame the optimizer. This is GH #683, #1010, and #1088.
125  volatile byte* unused = const_cast<volatile byte*>(outString);
126  CRYPTOPP_UNUSED(unused);
127 
128  inString = PtrAdd(inString, iterations * bytesPerIteration);
129  outString = PtrAdd(outString, iterations * bytesPerIteration);
130  length -= iterations * bytesPerIteration;
131  }
132 
133  size_t bufferByteSize = m_buffer.size();
134  size_t bufferIterations = bufferByteSize / bytesPerIteration;
135 
136  while (length >= bufferByteSize)
137  {
138  policy.WriteKeystream(m_buffer, bufferIterations);
139  xorbuf(outString, inString, KeystreamBufferBegin(), bufferByteSize);
140 
141  inString = PtrAdd(inString, bufferByteSize);
142  outString = PtrAdd(outString, bufferByteSize);
143  length -= bufferByteSize;
144  }
145 
146  if (length > 0)
147  {
148  bufferByteSize = RoundUpToMultipleOf(length, bytesPerIteration);
149  bufferIterations = bufferByteSize / bytesPerIteration;
150 
151  policy.WriteKeystream(PtrSub(KeystreamBufferEnd(), bufferByteSize), bufferIterations);
152  xorbuf(outString, inString, PtrSub(KeystreamBufferEnd(), bufferByteSize), length);
153 
154  m_leftOver = bufferByteSize - length;
155  }
156 }
157 
158 template <class S>
159 void AdditiveCipherTemplate<S>::Resynchronize(const byte *iv, int length)
160 {
161  PolicyInterface &policy = this->AccessPolicy();
162  m_leftOver = 0;
163  m_buffer.New(GetBufferByteSize(policy));
164  policy.CipherResynchronize(m_buffer, iv, this->ThrowIfInvalidIVLength(length));
165 }
166 
167 template <class BASE>
169 {
170  PolicyInterface &policy = this->AccessPolicy();
171  unsigned int bytesPerIteration = policy.GetBytesPerIteration();
172 
173  policy.SeekToIteration(position / bytesPerIteration);
174  position %= bytesPerIteration;
175 
176  if (position > 0)
177  {
178  policy.WriteKeystream(PtrSub(KeystreamBufferEnd(), bytesPerIteration), 1);
179  m_leftOver = bytesPerIteration - static_cast<unsigned int>(position);
180  }
181  else
182  m_leftOver = 0;
183 }
184 
185 template <class BASE>
186 void CFB_CipherTemplate<BASE>::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs &params)
187 {
188  PolicyInterface &policy = this->AccessPolicy();
189  policy.CipherSetKey(params, key, length);
190 
191  if (this->IsResynchronizable())
192  {
193  size_t ivLength;
194  const byte *iv = this->GetIVAndThrowIfInvalid(params, ivLength);
195  policy.CipherResynchronize(iv, ivLength);
196  }
197 
198  m_leftOver = policy.GetBytesPerIteration();
199 }
200 
201 template <class BASE>
202 void CFB_CipherTemplate<BASE>::Resynchronize(const byte *iv, int length)
203 {
204  PolicyInterface &policy = this->AccessPolicy();
205  policy.CipherResynchronize(iv, this->ThrowIfInvalidIVLength(length));
206  m_leftOver = policy.GetBytesPerIteration();
207 }
208 
209 // TODO: Figure out what is happening in ProcessData. The issue surfaced
210 // for CFB_CipherTemplate<BASE>::ProcessData when we cut-in Cryptogams
211 // AES ARMv7 asm. Then again in AdditiveCipherTemplate<S>::ProcessData
212 // for CTR mode with HIGHT, which is a 64-bit block cipher. In both cases,
213 // inString == outString leads to incorrect results. We think it relates
214 // to aliasing violations because inString == outString.
215 //
216 // Also see https://github.com/weidai11/cryptopp/issues/683,
217 // https://github.com/weidai11/cryptopp/issues/1010 and
218 // https://github.com/weidai11/cryptopp/issues/1088.
219 
220 template <class BASE>
221 void CFB_CipherTemplate<BASE>::ProcessData(byte *outString, const byte *inString, size_t length)
222 {
223  CRYPTOPP_ASSERT(outString); CRYPTOPP_ASSERT(inString);
224  CRYPTOPP_ASSERT(length % this->MandatoryBlockSize() == 0);
225 
226  PolicyInterface &policy = this->AccessPolicy();
227  unsigned int bytesPerIteration = policy.GetBytesPerIteration();
228  byte *reg = policy.GetRegisterBegin();
229 
230  if (m_leftOver)
231  {
232  const size_t len = STDMIN(m_leftOver, length);
233  CombineMessageAndShiftRegister(outString, PtrAdd(reg, bytesPerIteration - m_leftOver), inString, len);
234 
235  inString = PtrAdd(inString, len);
236  outString = PtrAdd(outString, len);
237  m_leftOver -= len; length -= len;
238  }
239 
240  if (!length) { return; }
241 
242  const word32 alignment = policy.GetAlignment();
243  const bool inAligned = IsAlignedOn(inString, alignment);
244  const bool outAligned = IsAlignedOn(outString, alignment);
245  CRYPTOPP_UNUSED(inAligned); CRYPTOPP_UNUSED(outAligned);
246 
247  if (policy.CanIterate() && length >= bytesPerIteration && outAligned)
248  {
249  CipherDir cipherDir = GetCipherDir(*this);
250  policy.Iterate(outString, inString, cipherDir, length / bytesPerIteration);
251 
252  // Try to tame the optimizer. This is GH #683, #1010, and #1088.
253  volatile byte* unused = const_cast<volatile byte*>(outString);
254  CRYPTOPP_UNUSED(unused);
255 
256  const size_t remainder = length % bytesPerIteration;
257  inString = PtrAdd(inString, length - remainder);
258  outString = PtrAdd(outString, length - remainder);
259  length = remainder;
260  }
261 
262  while (length >= bytesPerIteration)
263  {
264  policy.TransformRegister();
265  CombineMessageAndShiftRegister(outString, reg, inString, bytesPerIteration);
266 
267  inString = PtrAdd(inString, bytesPerIteration);
268  outString = PtrAdd(outString, bytesPerIteration);
269  length -= bytesPerIteration;
270  }
271 
272  if (length > 0)
273  {
274  policy.TransformRegister();
275  CombineMessageAndShiftRegister(outString, reg, inString, length);
276  m_leftOver = bytesPerIteration - length;
277  }
278 }
279 
280 template <class BASE>
281 void CFB_EncryptionTemplate<BASE>::CombineMessageAndShiftRegister(byte *output, byte *reg, const byte *message, size_t length)
282 {
283  xorbuf(reg, message, length);
284  std::memcpy(output, reg, length);
285 }
286 
287 template <class BASE>
288 void CFB_DecryptionTemplate<BASE>::CombineMessageAndShiftRegister(byte *output, byte *reg, const byte *message, size_t length)
289 {
290  for (size_t i=0; i<length; i++)
291  {
292  byte b = message[i];
293  output[i] = reg[i] ^ b;
294  reg[i] = b;
295  }
296 }
297 
298 NAMESPACE_END
299 
300 #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:568
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:664
Base class for feedback based stream ciphers in the forward direction with SymmetricCipher interface.
Definition: strciphr.h:655
Interface for retrieving values given their names.
Definition: cryptlib.h:327
unsigned int word32
32-bit unsigned datatype
Definition: config_int.h:72
word64 lword
Large word type.
Definition: config_int.h:168
CipherDir
Specifies a direction for a cipher to operate.
Definition: cryptlib.h:128
T1 RoundUpToMultipleOf(const T1 &n, const T2 &m)
Rounds a value up to a multiple of a second value.
Definition: misc.h:1384
PTR PtrSub(PTR pointer, OFF offset)
Create a pointer with an offset.
Definition: misc.h:401
bool IsAlignedOn(const void *ptr, unsigned int alignment)
Determines whether ptr is aligned to a minimum value.
Definition: misc.h:1436
PTR PtrAdd(PTR pointer, OFF offset)
Create a pointer with an offset.
Definition: misc.h:388
const T & STDMIN(const T &a, const T &b)
Replacement function for std::min.
Definition: misc.h:657
#define EnumToInt(v)
Integer value.
Definition: misc.h:504
CipherDir GetCipherDir(const T &obj)
Returns the direction the cipher is being operated.
Definition: misc.h:1497
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