104306f587
STRICT_ALIGNMENT is a remnant of OpenSSL code would cast pointers to size_t* and load more than one byte at a time. Not all architectures support unaligned access, so it did an alignment check and only enterred this path if aligned or the underlying architecture didn't care. This is UB. Unaligned casts in C are undefined on all architectures, so we switch these to memcpy some time ago. Compilers can optimize memcpy to the unaligned accesses we wanted. That left our modes logic as: - If STRICT_ALIGNMENT is 1 and things are unaligned, work byte-by-byte. - Otherwise, use the memcpy-based word-by-word code, which now works independent of STRICT_ALIGNMENT. Remove the first check to simplify things. On x86, x86_64, and aarch64, STRICT_ALIGNMENT is zero and this is a no-op. ARM is more complex. Per [0], ARMv7 and up support unaligned access. ARMv5 do not. ARMv6 does, but can run in a mode where it looks more like ARMv5. For ARMv7 and up, STRICT_ALIGNMENT should have been zero, but was one. Thus this change should be an improvement for ARMv7 (right now unaligned inputs lose bsaes-armv7). The Android NDK does not even support the pre-ARMv7 ABI anymore[1]. Nonetheless, Cronet still supports ARMv6 as a library. It builds with -march=armv6 which GCC interprets as supporting unaligned access, so it too did not want this code. For completeness, should anyone still care about ARMv5 or be building with an overly permissive -march flag, GCC does appear unable to inline the memcpy calls. However, GCC also does not interpret (uintptr_t)ptr % sizeof(size_t) as an alignment assertion, so such consumers have already been paying for the memcpy here and throughout the library. In general, C's arcane pointer rules mean we must resort to memcpy often, so, realistically, we must require that the compiler optimize memcpy well. [0] https://medium.com/@iLevex/the-curious-case-of-unaligned-access-on-arm-5dd0ebe24965 [1] https://developer.android.com/ndk/guides/abis#armeabi Change-Id: I3c7dea562adaeb663032e395499e69530dd8e145 Reviewed-on: https://boringssl-review.googlesource.com/c/34873 Reviewed-by: Adam Langley <agl@google.com>
203 lines
6.4 KiB
C
203 lines
6.4 KiB
C
/* ====================================================================
|
|
* Copyright (c) 2008 The OpenSSL Project. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* 3. All advertising materials mentioning features or use of this
|
|
* software must display the following acknowledgment:
|
|
* "This product includes software developed by the OpenSSL Project
|
|
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
|
|
*
|
|
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
|
|
* endorse or promote products derived from this software without
|
|
* prior written permission. For written permission, please contact
|
|
* openssl-core@openssl.org.
|
|
*
|
|
* 5. Products derived from this software may not be called "OpenSSL"
|
|
* nor may "OpenSSL" appear in their names without prior written
|
|
* permission of the OpenSSL Project.
|
|
*
|
|
* 6. Redistributions of any form whatsoever must retain the following
|
|
* acknowledgment:
|
|
* "This product includes software developed by the OpenSSL Project
|
|
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
|
|
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
|
|
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
* ==================================================================== */
|
|
|
|
#include <openssl/type_check.h>
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
OPENSSL_STATIC_ASSERT(16 % sizeof(size_t) == 0,
|
|
"block cannot be divided into size_t");
|
|
|
|
void CRYPTO_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
|
|
const AES_KEY *key, uint8_t ivec[16], unsigned *num,
|
|
int enc, block128_f block) {
|
|
assert(in && out && key && ivec && num);
|
|
|
|
unsigned n = *num;
|
|
|
|
if (enc) {
|
|
while (n && len) {
|
|
*(out++) = ivec[n] ^= *(in++);
|
|
--len;
|
|
n = (n + 1) % 16;
|
|
}
|
|
while (len >= 16) {
|
|
(*block)(ivec, ivec, key);
|
|
for (; n < 16; n += sizeof(size_t)) {
|
|
size_t tmp = load_word_le(ivec + n) ^ load_word_le(in + n);
|
|
store_word_le(ivec + n, tmp);
|
|
store_word_le(out + n, tmp);
|
|
}
|
|
len -= 16;
|
|
out += 16;
|
|
in += 16;
|
|
n = 0;
|
|
}
|
|
if (len) {
|
|
(*block)(ivec, ivec, key);
|
|
while (len--) {
|
|
out[n] = ivec[n] ^= in[n];
|
|
++n;
|
|
}
|
|
}
|
|
*num = n;
|
|
return;
|
|
} else {
|
|
while (n && len) {
|
|
uint8_t c;
|
|
*(out++) = ivec[n] ^ (c = *(in++));
|
|
ivec[n] = c;
|
|
--len;
|
|
n = (n + 1) % 16;
|
|
}
|
|
while (len >= 16) {
|
|
(*block)(ivec, ivec, key);
|
|
for (; n < 16; n += sizeof(size_t)) {
|
|
size_t t = load_word_le(in + n);
|
|
store_word_le(out + n, load_word_le(ivec + n) ^ t);
|
|
store_word_le(ivec + n, t);
|
|
}
|
|
len -= 16;
|
|
out += 16;
|
|
in += 16;
|
|
n = 0;
|
|
}
|
|
if (len) {
|
|
(*block)(ivec, ivec, key);
|
|
while (len--) {
|
|
uint8_t c;
|
|
out[n] = ivec[n] ^ (c = in[n]);
|
|
ivec[n] = c;
|
|
++n;
|
|
}
|
|
}
|
|
*num = n;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/* This expects a single block of size nbits for both in and out. Note that
|
|
it corrupts any extra bits in the last byte of out */
|
|
static void cfbr_encrypt_block(const uint8_t *in, uint8_t *out, unsigned nbits,
|
|
const AES_KEY *key, uint8_t ivec[16], int enc,
|
|
block128_f block) {
|
|
int n, rem, num;
|
|
uint8_t ovec[16 * 2 + 1]; /* +1 because we dererefence (but don't use) one
|
|
byte off the end */
|
|
|
|
if (nbits <= 0 || nbits > 128) {
|
|
return;
|
|
}
|
|
|
|
// fill in the first half of the new IV with the current IV
|
|
OPENSSL_memcpy(ovec, ivec, 16);
|
|
// construct the new IV
|
|
(*block)(ivec, ivec, key);
|
|
num = (nbits + 7) / 8;
|
|
if (enc) {
|
|
// encrypt the input
|
|
for (n = 0; n < num; ++n) {
|
|
out[n] = (ovec[16 + n] = in[n] ^ ivec[n]);
|
|
}
|
|
} else {
|
|
// decrypt the input
|
|
for (n = 0; n < num; ++n) {
|
|
out[n] = (ovec[16 + n] = in[n]) ^ ivec[n];
|
|
}
|
|
}
|
|
// shift ovec left...
|
|
rem = nbits % 8;
|
|
num = nbits / 8;
|
|
if (rem == 0) {
|
|
OPENSSL_memcpy(ivec, ovec + num, 16);
|
|
} else {
|
|
for (n = 0; n < 16; ++n) {
|
|
ivec[n] = ovec[n + num] << rem | ovec[n + num + 1] >> (8 - rem);
|
|
}
|
|
}
|
|
|
|
// it is not necessary to cleanse ovec, since the IV is not secret
|
|
}
|
|
|
|
// N.B. This expects the input to be packed, MS bit first
|
|
void CRYPTO_cfb128_1_encrypt(const uint8_t *in, uint8_t *out, size_t bits,
|
|
const AES_KEY *key, uint8_t ivec[16],
|
|
unsigned *num, int enc, block128_f block) {
|
|
size_t n;
|
|
uint8_t c[1], d[1];
|
|
|
|
assert(in && out && key && ivec && num);
|
|
assert(*num == 0);
|
|
|
|
for (n = 0; n < bits; ++n) {
|
|
c[0] = (in[n / 8] & (1 << (7 - n % 8))) ? 0x80 : 0;
|
|
cfbr_encrypt_block(c, d, 1, key, ivec, enc, block);
|
|
out[n / 8] = (out[n / 8] & ~(1 << (unsigned int)(7 - n % 8))) |
|
|
((d[0] & 0x80) >> (unsigned int)(n % 8));
|
|
}
|
|
}
|
|
|
|
void CRYPTO_cfb128_8_encrypt(const unsigned char *in, unsigned char *out,
|
|
size_t length, const AES_KEY *key,
|
|
unsigned char ivec[16], unsigned *num, int enc,
|
|
block128_f block) {
|
|
size_t n;
|
|
|
|
assert(in && out && key && ivec && num);
|
|
assert(*num == 0);
|
|
|
|
for (n = 0; n < length; ++n) {
|
|
cfbr_encrypt_block(&in[n], &out[n], 8, key, ivec, enc, block);
|
|
}
|
|
}
|