void CFB_CipherTemplate<BASE>::ProcessData(byte *outString, const byte *inString, unsigned int length) { assert(length % this->MandatoryBlockSize() == 0); PolicyInterface &policy = this->AccessPolicy(); unsigned int bytesPerIteration = policy.GetBytesPerIteration(); unsigned int alignment = policy.GetAlignment(); byte *reg = policy.GetRegisterBegin(); if (m_leftOver) { unsigned int len = STDMIN(m_leftOver, length); CombineMessageAndShiftRegister(outString, reg + bytesPerIteration - m_leftOver, inString, len); m_leftOver -= len; length -= len; inString += len; outString += len; } if (!length) return; assert(m_leftOver == 0); if (policy.CanIterate() && length >= bytesPerIteration && IsAlignedOn(outString, alignment)) { if (IsAlignedOn(inString, alignment)) policy.Iterate(outString, inString, GetCipherDir(*this), length / bytesPerIteration); else { memcpy(outString, inString, length); policy.Iterate(outString, outString, GetCipherDir(*this), length / bytesPerIteration); } inString += length - length % bytesPerIteration; outString += length - length % bytesPerIteration; length %= bytesPerIteration; } while (length >= bytesPerIteration) { policy.TransformRegister(); CombineMessageAndShiftRegister(outString, reg, inString, bytesPerIteration); length -= bytesPerIteration; inString += bytesPerIteration; outString += bytesPerIteration; } if (length > 0) { policy.TransformRegister(); CombineMessageAndShiftRegister(outString, reg, inString, length); m_leftOver = bytesPerIteration - length; } }
void CFB_CipherTemplate<BASE>::ProcessData(byte *outString, const byte *inString, size_t length) { CRYPTOPP_ASSERT(outString); CRYPTOPP_ASSERT(inString); CRYPTOPP_ASSERT(length % this->MandatoryBlockSize() == 0); PolicyInterface &policy = this->AccessPolicy(); word32 bytesPerIteration = policy.GetBytesPerIteration(); byte *reg = policy.GetRegisterBegin(); if (m_leftOver) { const size_t len = STDMIN(m_leftOver, length); CombineMessageAndShiftRegister(outString, PtrAdd(reg, bytesPerIteration - m_leftOver), inString, len); m_leftOver -= len; length -= len; inString = PtrAdd(inString, len); outString = PtrAdd(outString, len); } // TODO: Figure out what is happening on ARM A-32. x86, Aarch64 and PowerPC are OK. // The issue surfaced for CFB mode when we cut-in Cryptogams AES ARMv7 asm. // Using 'outString' for both input and output leads to incorrect results. // // Benchmarking on Cortex-A7 and Cortex-A9 indicates removing the block // below costs about 9 cpb for CFB mode on ARM. // // Also see https://github.com/weidai11/cryptopp/issues/683. // // UPDATE: It appears the issue is related to alignment checks. When we made // the alignment check result volatile GCC and Clang stopped short- // circuiting the transform, which is what we wanted. I suspect // there's a little more to the issue, but we can enable the block again. const unsigned int alignment = policy.GetAlignment(); volatile bool isAligned = IsAlignedOn(outString, alignment); if (policy.CanIterate() && length >= bytesPerIteration && isAligned) { isAligned &= IsAlignedOn(inString, alignment); const CipherDir cipherDir = GetCipherDir(*this); if (isAligned) policy.Iterate(outString, inString, cipherDir, length / bytesPerIteration); else { // GCC and Clang does not like this on ARM. The incorrect result is a string // of 0's instead of ciphertext (or plaintext if decrypting). The 0's trace // back to the allocation for the std::string in datatest.cpp. Elements in the // string are initialized to their default value, which is 0. // // It almost feels as if the compiler does not see the string is transformed // in-place so it short-circuits the transform. However, if we use a stand-alone // reproducer with the same data then the issue is _not_ present. // // When working on this issue we introduced PtrAdd and PtrSub to ensure we were // not running afoul of pointer arithmetic rules of the language. Namely we need // to use ptrdiff_t when subtracting pointers. We believe the relevant code paths // are clean. // // One workaround is a distinct and aligned temporary buffer. It [mostly] works // as expected but requires an extra allocation (casts not shown): // // std::string temp(inString, length); // policy.Iterate(outString, &temp[0], cipherDir, length / bytesPerIteration); // memcpy(outString, inString, length); policy.Iterate(outString, outString, cipherDir, length / bytesPerIteration); } const size_t remainder = length % bytesPerIteration; inString = PtrAdd(inString, length - remainder); outString = PtrAdd(outString, length - remainder); length = remainder; } while (length >= bytesPerIteration) { policy.TransformRegister(); CombineMessageAndShiftRegister(outString, reg, inString, bytesPerIteration); length -= bytesPerIteration; inString = PtrAdd(inString, bytesPerIteration); outString = PtrAdd(outString, bytesPerIteration); } if (length > 0) { policy.TransformRegister(); CombineMessageAndShiftRegister(outString, reg, inString, length); m_leftOver = bytesPerIteration - length; } }