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
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;
119 KeystreamOperationFlags flags = static_cast<KeystreamOperationFlags>(
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>
168 void AdditiveCipherTemplate<BASE>::Seek(lword position)
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
AdditiveCipherTemplate
Base class for additive stream ciphers with SymmetricCipher interface.
Definition: strciphr.h:298
AdditiveCipherTemplate::ProcessData
void ProcessData(byte *outString, const byte *inString, size_t length)
Apply keystream to data.
AdditiveCipherTemplate::GenerateBlock
void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
AdditiveCipherTemplate::Seek
void Seek(lword position)
Seeks to a random position in the stream.
AdditiveCipherTemplate::Resynchronize
void Resynchronize(const byte *iv, int length=-1)
Resynchronize the cipher.
CFB_CipherTemplate
Base class for feedback based stream ciphers with SymmetricCipher interface.
Definition: strciphr.h:568
CFB_CipherTemplate::Resynchronize
void Resynchronize(const byte *iv, int length=-1)
Resynchronize the cipher.
CFB_CipherTemplate::ProcessData
void ProcessData(byte *outString, const byte *inString, size_t length)
Apply keystream to data.
CFB_DecryptionTemplate
Base class for feedback based stream ciphers in the reverse direction with SymmetricCipher interface.
Definition: strciphr.h:664
CFB_EncryptionTemplate
Base class for feedback based stream ciphers in the forward direction with SymmetricCipher interface.
Definition: strciphr.h:655
NameValuePairs
Interface for retrieving values given their names.
Definition: cryptlib.h:327
word32
unsigned int word32
32-bit unsigned datatype
Definition: config_int.h:72
lword
word64 lword
Large word type.
Definition: config_int.h:168
CipherDir
CipherDir
Specifies a direction for a cipher to operate.
Definition: cryptlib.h:128
RoundUpToMultipleOf
T1 RoundUpToMultipleOf(const T1 &n, const T2 &m)
Rounds a value up to a multiple of a second value.
Definition: misc.h:1377
PtrSub
PTR PtrSub(PTR pointer, OFF offset)
Create a pointer with an offset.
Definition: misc.h:401
IsAlignedOn
bool IsAlignedOn(const void *ptr, unsigned int alignment)
Determines whether ptr is aligned to a minimum value.
Definition: misc.h:1429
PtrAdd
PTR PtrAdd(PTR pointer, OFF offset)
Create a pointer with an offset.
Definition: misc.h:388
STDMIN
const T & STDMIN(const T &a, const T &b)
Replacement function for std::min.
Definition: misc.h:657
EnumToInt
#define EnumToInt(v)
Integer value.
Definition: misc.h:504
GetCipherDir
CipherDir GetCipherDir(const T &obj)
Returns the direction the cipher is being operated.
Definition: misc.h:1490
xorbuf
CRYPTOPP_DLL void xorbuf(byte *buf, const byte *mask, size_t count)
Performs an XOR of a buffer with a mask.
CryptoPP
Crypto++ library namespace.
pch.h
Precompiled header file.
strciphr.h
Classes for implementing stream ciphers.
KeystreamOperation
KeystreamOperation
Keystream operation flags.
Definition: strciphr.h:88
KeystreamOperationFlags
KeystreamOperationFlags
Keystream operation flags.
Definition: strciphr.h:76
INPUT_ALIGNED
@ INPUT_ALIGNED
Input buffer is aligned.
Definition: strciphr.h:80
OUTPUT_ALIGNED
@ OUTPUT_ALIGNED
Output buffer is aligned.
Definition: strciphr.h:78
CRYPTOPP_ASSERT
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition: trap.h:68