Fixed IV processing for CBC decryption with AES (x86ni implementation) when data...
[BearSSL] / src / inner.h
index bcf61b0..3830c7c 100644 (file)
  * Determine whether x86 AES instructions are understood by the compiler.
  */
 #ifndef BR_AES_X86NI
  * Determine whether x86 AES instructions are understood by the compiler.
  */
 #ifndef BR_AES_X86NI
-
 #if (__i386__ || __x86_64__) \
        && ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) \
            || (__clang_major__ > 3 \
 #if (__i386__ || __x86_64__) \
        && ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) \
            || (__clang_major__ > 3 \
  * GCC versions from 4.4 to 4.8 (inclusive) must use a special #pragma
  * to activate extra opcodes before including the relevant intrinsic
  * headers. But these don't work with Clang (which does not need them
  * GCC versions from 4.4 to 4.8 (inclusive) must use a special #pragma
  * to activate extra opcodes before including the relevant intrinsic
  * headers. But these don't work with Clang (which does not need them
- * either).
+ * either). We also need that #pragma for GCC 4.9 in order to work
+ * around a compiler bug (it tends to blow up on ghash_pclmul code
+ * otherwise).
  */
 #if BR_AES_X86NI_GCC && !defined BR_AES_X86NI_GCC_OLD
  */
 #if BR_AES_X86NI_GCC && !defined BR_AES_X86NI_GCC_OLD
-#if __GNUC__ == 4 && __GNUC_MINOR__ >= 4 && __GNUC_MINOR__ <= 8 && !__clang__
+#if __GNUC__ == 4 && __GNUC_MINOR__ >= 4 && __GNUC_MINOR__ <= 9 && !__clang__
 #define BR_AES_X86NI_GCC_OLD   1
 #endif
 #endif
 #define BR_AES_X86NI_GCC_OLD   1
 #endif
 #endif
 #endif
 #endif
 
 #endif
 #endif
 
+/*
+ * Detect support for unaligned accesses with known endianness.
+ *
+ *  x86 (both 32-bit and 64-bit) is little-endian and allows unaligned
+ *  accesses.
+ *
+ *  POWER/PowerPC allows unaligned accesses when big-endian. POWER8 and
+ *  later also allow unaligned accesses when little-endian.
+ */
+#if !defined BR_LE_UNALIGNED && !defined BR_BE_UNALIGNED
+
+#if __i386 || __i386__ || __x86_64__ || _M_IX86 || _M_X64
+#define BR_LE_UNALIGNED   1
+#elif BR_POWER8_BE
+#define BR_BE_UNALIGNED   1
+#elif BR_POWER8_LE
+#define BR_LE_UNALIGNED   1
+#elif (__powerpc__ || __powerpc64__ || _M_PPC || _ARCH_PPC || _ARCH_PPC64) \
+       && __BIG_ENDIAN__
+#define BR_BE_UNALIGNED   1
+#endif
+
+#endif
+
 /* ==================================================================== */
 /*
  * Encoding/decoding functions.
  *
  * 32-bit and 64-bit decoding, both little-endian and big-endian, is
 /* ==================================================================== */
 /*
  * Encoding/decoding functions.
  *
  * 32-bit and 64-bit decoding, both little-endian and big-endian, is
- * implemented with the inline functions below. These functions are
- * generic: they don't depend on the architecture natural endianness,
- * and they can handle unaligned accesses. Optimized versions for some
- * specific architectures may be implemented at a later time.
- */
+ * implemented with the inline functions below.
+ *
+ * When allowed by some compile-time options (autodetected or provided),
+ * optimised code is used, to perform direct memory access when the
+ * underlying architecture supports it, both for endianness and
+ * alignment. This, however, may trigger strict aliasing issues; the
+ * code below uses unions to perform (supposedly) safe type punning.
+ * Since the C aliasing rules are relatively complex and were amended,
+ * or at least re-explained with different phrasing, in all successive
+ * versions of the C standard, it is always a bit risky to bet that any
+ * specific version of a C compiler got it right, for some notion of
+ * "right".
+ */
+
+typedef union {
+       uint16_t u;
+       unsigned char b[sizeof(uint16_t)];
+} br_union_u16;
+
+typedef union {
+       uint32_t u;
+       unsigned char b[sizeof(uint32_t)];
+} br_union_u32;
+
+typedef union {
+       uint64_t u;
+       unsigned char b[sizeof(uint64_t)];
+} br_union_u64;
 
 static inline void
 br_enc16le(void *dst, unsigned x)
 {
 
 static inline void
 br_enc16le(void *dst, unsigned x)
 {
+#if BR_LE_UNALIGNED
+       ((br_union_u16 *)dst)->u = x;
+#else
        unsigned char *buf;
 
        buf = dst;
        buf[0] = (unsigned char)x;
        buf[1] = (unsigned char)(x >> 8);
        unsigned char *buf;
 
        buf = dst;
        buf[0] = (unsigned char)x;
        buf[1] = (unsigned char)(x >> 8);
+#endif
 }
 
 static inline void
 br_enc16be(void *dst, unsigned x)
 {
 }
 
 static inline void
 br_enc16be(void *dst, unsigned x)
 {
+#if BR_BE_UNALIGNED
+       ((br_union_u16 *)dst)->u = x;
+#else
        unsigned char *buf;
 
        buf = dst;
        buf[0] = (unsigned char)(x >> 8);
        buf[1] = (unsigned char)x;
        unsigned char *buf;
 
        buf = dst;
        buf[0] = (unsigned char)(x >> 8);
        buf[1] = (unsigned char)x;
+#endif
 }
 
 static inline unsigned
 br_dec16le(const void *src)
 {
 }
 
 static inline unsigned
 br_dec16le(const void *src)
 {
+#if BR_LE_UNALIGNED
+       return ((const br_union_u16 *)src)->u;
+#else
        const unsigned char *buf;
 
        buf = src;
        return (unsigned)buf[0] | ((unsigned)buf[1] << 8);
        const unsigned char *buf;
 
        buf = src;
        return (unsigned)buf[0] | ((unsigned)buf[1] << 8);
+#endif
 }
 
 static inline unsigned
 br_dec16be(const void *src)
 {
 }
 
 static inline unsigned
 br_dec16be(const void *src)
 {
+#if BR_BE_UNALIGNED
+       return ((const br_union_u16 *)src)->u;
+#else
        const unsigned char *buf;
 
        buf = src;
        return ((unsigned)buf[0] << 8) | (unsigned)buf[1];
        const unsigned char *buf;
 
        buf = src;
        return ((unsigned)buf[0] << 8) | (unsigned)buf[1];
+#endif
 }
 
 static inline void
 br_enc32le(void *dst, uint32_t x)
 {
 }
 
 static inline void
 br_enc32le(void *dst, uint32_t x)
 {
+#if BR_LE_UNALIGNED
+       ((br_union_u32 *)dst)->u = x;
+#else
        unsigned char *buf;
 
        buf = dst;
        unsigned char *buf;
 
        buf = dst;
@@ -279,11 +346,15 @@ br_enc32le(void *dst, uint32_t x)
        buf[1] = (unsigned char)(x >> 8);
        buf[2] = (unsigned char)(x >> 16);
        buf[3] = (unsigned char)(x >> 24);
        buf[1] = (unsigned char)(x >> 8);
        buf[2] = (unsigned char)(x >> 16);
        buf[3] = (unsigned char)(x >> 24);
+#endif
 }
 
 static inline void
 br_enc32be(void *dst, uint32_t x)
 {
 }
 
 static inline void
 br_enc32be(void *dst, uint32_t x)
 {
+#if BR_BE_UNALIGNED
+       ((br_union_u32 *)dst)->u = x;
+#else
        unsigned char *buf;
 
        buf = dst;
        unsigned char *buf;
 
        buf = dst;
@@ -291,11 +362,15 @@ br_enc32be(void *dst, uint32_t x)
        buf[1] = (unsigned char)(x >> 16);
        buf[2] = (unsigned char)(x >> 8);
        buf[3] = (unsigned char)x;
        buf[1] = (unsigned char)(x >> 16);
        buf[2] = (unsigned char)(x >> 8);
        buf[3] = (unsigned char)x;
+#endif
 }
 
 static inline uint32_t
 br_dec32le(const void *src)
 {
 }
 
 static inline uint32_t
 br_dec32le(const void *src)
 {
+#if BR_LE_UNALIGNED
+       return ((const br_union_u32 *)src)->u;
+#else
        const unsigned char *buf;
 
        buf = src;
        const unsigned char *buf;
 
        buf = src;
@@ -303,11 +378,15 @@ br_dec32le(const void *src)
                | ((uint32_t)buf[1] << 8)
                | ((uint32_t)buf[2] << 16)
                | ((uint32_t)buf[3] << 24);
                | ((uint32_t)buf[1] << 8)
                | ((uint32_t)buf[2] << 16)
                | ((uint32_t)buf[3] << 24);
+#endif
 }
 
 static inline uint32_t
 br_dec32be(const void *src)
 {
 }
 
 static inline uint32_t
 br_dec32be(const void *src)
 {
+#if BR_BE_UNALIGNED
+       return ((const br_union_u32 *)src)->u;
+#else
        const unsigned char *buf;
 
        buf = src;
        const unsigned char *buf;
 
        buf = src;
@@ -315,46 +394,63 @@ br_dec32be(const void *src)
                | ((uint32_t)buf[1] << 16)
                | ((uint32_t)buf[2] << 8)
                | (uint32_t)buf[3];
                | ((uint32_t)buf[1] << 16)
                | ((uint32_t)buf[2] << 8)
                | (uint32_t)buf[3];
+#endif
 }
 
 static inline void
 br_enc64le(void *dst, uint64_t x)
 {
 }
 
 static inline void
 br_enc64le(void *dst, uint64_t x)
 {
+#if BR_LE_UNALIGNED
+       ((br_union_u64 *)dst)->u = x;
+#else
        unsigned char *buf;
 
        buf = dst;
        br_enc32le(buf, (uint32_t)x);
        br_enc32le(buf + 4, (uint32_t)(x >> 32));
        unsigned char *buf;
 
        buf = dst;
        br_enc32le(buf, (uint32_t)x);
        br_enc32le(buf + 4, (uint32_t)(x >> 32));
+#endif
 }
 
 static inline void
 br_enc64be(void *dst, uint64_t x)
 {
 }
 
 static inline void
 br_enc64be(void *dst, uint64_t x)
 {
+#if BR_BE_UNALIGNED
+       ((br_union_u64 *)dst)->u = x;
+#else
        unsigned char *buf;
 
        buf = dst;
        br_enc32be(buf, (uint32_t)(x >> 32));
        br_enc32be(buf + 4, (uint32_t)x);
        unsigned char *buf;
 
        buf = dst;
        br_enc32be(buf, (uint32_t)(x >> 32));
        br_enc32be(buf + 4, (uint32_t)x);
+#endif
 }
 
 static inline uint64_t
 br_dec64le(const void *src)
 {
 }
 
 static inline uint64_t
 br_dec64le(const void *src)
 {
+#if BR_LE_UNALIGNED
+       return ((const br_union_u64 *)src)->u;
+#else
        const unsigned char *buf;
 
        buf = src;
        return (uint64_t)br_dec32le(buf)
                | ((uint64_t)br_dec32le(buf + 4) << 32);
        const unsigned char *buf;
 
        buf = src;
        return (uint64_t)br_dec32le(buf)
                | ((uint64_t)br_dec32le(buf + 4) << 32);
+#endif
 }
 
 static inline uint64_t
 br_dec64be(const void *src)
 {
 }
 
 static inline uint64_t
 br_dec64be(const void *src)
 {
+#if BR_BE_UNALIGNED
+       return ((const br_union_u64 *)src)->u;
+#else
        const unsigned char *buf;
 
        buf = src;
        return ((uint64_t)br_dec32be(buf) << 32)
                | (uint64_t)br_dec32be(buf + 4);
        const unsigned char *buf;
 
        buf = src;
        return ((uint64_t)br_dec32be(buf) << 32)
                | (uint64_t)br_dec32be(buf + 4);
+#endif
 }
 
 /*
 }
 
 /*
@@ -1177,6 +1273,25 @@ void br_i31_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i);
 void br_i31_modpow(uint32_t *x, const unsigned char *e, size_t elen,
        const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2);
 
 void br_i31_modpow(uint32_t *x, const unsigned char *e, size_t elen,
        const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2);
 
+/*
+ * Compute a modular exponentiation. x[] MUST be an integer modulo m[]
+ * (same announced bit length, lower value). m[] MUST be odd. The
+ * exponent is in big-endian unsigned notation, over 'elen' bytes. The
+ * "m0i" parameter is equal to -(1/m0) mod 2^31, where m0 is the least
+ * significant value word of m[] (this works only if m[] is an odd
+ * integer). The tmp[] array is used for temporaries, and has size
+ * 'twlen' words; it must be large enough to accommodate at least two
+ * temporary values with the same size as m[] (including the leading
+ * "bit length" word). If there is room for more temporaries, then this
+ * function may use the extra room for window-based optimisation,
+ * resulting in faster computations.
+ *
+ * Returned value is 1 on success, 0 on error. An error is reported if
+ * the provided tmp[] array is too short.
+ */
+uint32_t br_i31_modpow_opt(uint32_t *x, const unsigned char *e, size_t elen,
+       const uint32_t *m, uint32_t m0i, uint32_t *tmp, size_t twlen);
+
 /*
  * Compute d+a*b, result in d. The initial announced bit length of d[]
  * MUST match that of a[]. The d[] array MUST be large enough to
 /*
  * Compute d+a*b, result in d. The initial announced bit length of d[]
  * MUST match that of a[]. The d[] array MUST be large enough to
@@ -1244,6 +1359,9 @@ void br_i15_reduce(uint16_t *x, const uint16_t *a, const uint16_t *m);
 
 void br_i15_mulacc(uint16_t *d, const uint16_t *a, const uint16_t *b);
 
 
 void br_i15_mulacc(uint16_t *d, const uint16_t *a, const uint16_t *b);
 
+uint32_t br_i62_modpow_opt(uint32_t *x31, const unsigned char *e, size_t elen,
+       const uint32_t *m31, uint32_t m0i31, uint64_t *tmp, size_t twlen);
+
 /* ==================================================================== */
 
 static inline size_t
 /* ==================================================================== */
 
 static inline size_t