X-Git-Url: https://bearssl.org/gitweb//home/git/?p=BearSSL;a=blobdiff_plain;f=src%2Finner.h;h=fb7f4a4f2d0b9b24a25365c8adecb00067f16228;hp=472dc2e6f1099539a9371595f4e74db5b45e6d7b;hb=ea95d8264c6aefe742a9c3f4f9d834b188566a29;hpb=5f045c759957fdff8c85716e6af99e10901fdac0 diff --git a/src/inner.h b/src/inner.h index 472dc2e..fb7f4a4 100644 --- a/src/inner.h +++ b/src/inner.h @@ -118,7 +118,6 @@ * 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 \ @@ -154,58 +153,192 @@ #define BR_TARGET(x) #endif +/* + * 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). 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 __GNUC__ == 4 && __GNUC_MINOR__ >= 4 && __GNUC_MINOR__ <= 9 && !__clang__ +#define BR_AES_X86NI_GCC_OLD 1 +#endif +#endif + +/* + * POWER8 crypto support. We rely on compiler macros for the + * architecture, since we do not have a reliable, simple way to detect + * the required support at runtime (we could try running an opcode, and + * trapping the exception or signal on illegal instruction, but this + * induces some non-trivial OS dependencies that we would prefer to + * avoid if possible). + */ +#ifndef BR_POWER8 +#if __GNUC__ && ((_ARCH_PWR8 || _ARCH_PPC) && __CRYPTO__) +#define BR_POWER8 1 +#endif +#endif + +/* + * Detect endinanness on POWER8. + */ +#if BR_POWER8 +#if defined BR_POWER8_LE +#undef BR_POWER8_BE +#if BR_POWER8_LE +#define BR_POWER8_BE 0 +#else +#define BR_POWER8_BE 1 +#endif +#elif defined BR_POWER8_BE +#undef BR_POWER8_LE +#if BR_POWER8_BE +#define BR_POWER8_LE 0 +#else +#define BR_POWER8_LE 1 +#endif +#else +#if __LITTLE_ENDIAN__ +#define BR_POWER8_LE 1 +#define BR_POWER8_BE 0 +#else +#define BR_POWER8_LE 0 +#define BR_POWER8_BE 1 +#endif +#endif +#endif + +/* + * Detect support for 128-bit integers. + */ +#if !defined BR_INT128 && !defined BR_UMUL128 +#ifdef __SIZEOF_INT128__ +#define BR_INT128 1 +#elif _M_X64 +#define BR_UMUL128 1 +#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 - * 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) { +#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); +#endif } 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; +#endif } 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); +#endif } 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]; +#endif } 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; @@ -213,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); +#endif } 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; @@ -225,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; +#endif } 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; @@ -237,11 +378,15 @@ br_dec32le(const void *src) | ((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) { +#if BR_BE_UNALIGNED + return ((const br_union_u32 *)src)->u; +#else const unsigned char *buf; buf = src; @@ -249,46 +394,63 @@ br_dec32be(const void *src) | ((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) { +#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)); +#endif } 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); +#endif } 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); +#endif } 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); +#endif } /* @@ -347,8 +509,8 @@ void br_sha2small_round(const unsigned char *buf, uint32_t *val); */ void br_tls_phash(void *dst, size_t len, const br_hash_class *dig, - const void *secret, size_t secret_len, - const char *label, const void *seed, size_t seed_len); + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed); /* * Copy all configured hash implementations from a multihash context @@ -1111,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); +/* + * 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 @@ -1178,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); +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 @@ -1498,6 +1682,19 @@ unsigned br_aes_x86ni_keysched_enc(unsigned char *skni, unsigned br_aes_x86ni_keysched_dec(unsigned char *skni, const void *key, size_t len); +/* + * Test support for AES POWER8 opcodes. + */ +int br_aes_pwr8_supported(void); + +/* + * AES key schedule, using POWER8 instructions. This yields the + * subkeys in the encryption direction. Number of rounds is returned. + * Key size MUST be 16, 24 or 32 bytes; otherwise, 0 is returned. + */ +unsigned br_aes_pwr8_keysched(unsigned char *skni, + const void *key, size_t len); + /* ==================================================================== */ /* * RSA. @@ -1775,4 +1972,85 @@ int br_ssl_choose_hash(unsigned bf); /* ==================================================================== */ +/* + * PowerPC / POWER assembly stuff. The special BR_POWER_ASM_MACROS macro + * must be defined before including this file; this is done by source + * files that use some inline assembly for PowerPC / POWER machines. + */ + +#if BR_POWER_ASM_MACROS + +#define lxvw4x(xt, ra, rb) lxvw4x_(xt, ra, rb) +#define stxvw4x(xt, ra, rb) stxvw4x_(xt, ra, rb) + +#define bdnz(foo) bdnz_(foo) +#define beq(foo) beq_(foo) + +#define li(rx, value) li_(rx, value) +#define addi(rx, ra, imm) addi_(rx, ra, imm) +#define cmpldi(rx, imm) cmpldi_(rx, imm) +#define mtctr(rx) mtctr_(rx) +#define vspltb(vrt, vrb, uim) vspltb_(vrt, vrb, uim) +#define vspltw(vrt, vrb, uim) vspltw_(vrt, vrb, uim) +#define vspltisb(vrt, imm) vspltisb_(vrt, imm) +#define vspltisw(vrt, imm) vspltisw_(vrt, imm) +#define vrlw(vrt, vra, vrb) vrlw_(vrt, vra, vrb) +#define vsbox(vrt, vra) vsbox_(vrt, vra) +#define vxor(vrt, vra, vrb) vxor_(vrt, vra, vrb) +#define vand(vrt, vra, vrb) vand_(vrt, vra, vrb) +#define vsro(vrt, vra, vrb) vsro_(vrt, vra, vrb) +#define vsl(vrt, vra, vrb) vsl_(vrt, vra, vrb) +#define vsldoi(vt, va, vb, sh) vsldoi_(vt, va, vb, sh) +#define vsr(vrt, vra, vrb) vsr_(vrt, vra, vrb) +#define vadduwm(vrt, vra, vrb) vadduwm_(vrt, vra, vrb) +#define vsububm(vrt, vra, vrb) vsububm_(vrt, vra, vrb) +#define vsubuwm(vrt, vra, vrb) vsubuwm_(vrt, vra, vrb) +#define vsrw(vrt, vra, vrb) vsrw_(vrt, vra, vrb) +#define vcipher(vt, va, vb) vcipher_(vt, va, vb) +#define vcipherlast(vt, va, vb) vcipherlast_(vt, va, vb) +#define vncipher(vt, va, vb) vncipher_(vt, va, vb) +#define vncipherlast(vt, va, vb) vncipherlast_(vt, va, vb) +#define vperm(vt, va, vb, vc) vperm_(vt, va, vb, vc) +#define vpmsumd(vt, va, vb) vpmsumd_(vt, va, vb) +#define xxpermdi(vt, va, vb, d) xxpermdi_(vt, va, vb, d) + +#define lxvw4x_(xt, ra, rb) "\tlxvw4x\t" #xt "," #ra "," #rb "\n" +#define stxvw4x_(xt, ra, rb) "\tstxvw4x\t" #xt "," #ra "," #rb "\n" + +#define label(foo) #foo "%=:\n" +#define bdnz_(foo) "\tbdnz\t" #foo "%=\n" +#define beq_(foo) "\tbeq\t" #foo "%=\n" + +#define li_(rx, value) "\tli\t" #rx "," #value "\n" +#define addi_(rx, ra, imm) "\taddi\t" #rx "," #ra "," #imm "\n" +#define cmpldi_(rx, imm) "\tcmpldi\t" #rx "," #imm "\n" +#define mtctr_(rx) "\tmtctr\t" #rx "\n" +#define vspltb_(vrt, vrb, uim) "\tvspltb\t" #vrt "," #vrb "," #uim "\n" +#define vspltw_(vrt, vrb, uim) "\tvspltw\t" #vrt "," #vrb "," #uim "\n" +#define vspltisb_(vrt, imm) "\tvspltisb\t" #vrt "," #imm "\n" +#define vspltisw_(vrt, imm) "\tvspltisw\t" #vrt "," #imm "\n" +#define vrlw_(vrt, vra, vrb) "\tvrlw\t" #vrt "," #vra "," #vrb "\n" +#define vsbox_(vrt, vra) "\tvsbox\t" #vrt "," #vra "\n" +#define vxor_(vrt, vra, vrb) "\tvxor\t" #vrt "," #vra "," #vrb "\n" +#define vand_(vrt, vra, vrb) "\tvand\t" #vrt "," #vra "," #vrb "\n" +#define vsro_(vrt, vra, vrb) "\tvsro\t" #vrt "," #vra "," #vrb "\n" +#define vsl_(vrt, vra, vrb) "\tvsl\t" #vrt "," #vra "," #vrb "\n" +#define vsldoi_(vt, va, vb, sh) "\tvsldoi\t" #vt "," #va "," #vb "," #sh "\n" +#define vsr_(vrt, vra, vrb) "\tvsr\t" #vrt "," #vra "," #vrb "\n" +#define vadduwm_(vrt, vra, vrb) "\tvadduwm\t" #vrt "," #vra "," #vrb "\n" +#define vsububm_(vrt, vra, vrb) "\tvsububm\t" #vrt "," #vra "," #vrb "\n" +#define vsubuwm_(vrt, vra, vrb) "\tvsubuwm\t" #vrt "," #vra "," #vrb "\n" +#define vsrw_(vrt, vra, vrb) "\tvsrw\t" #vrt "," #vra "," #vrb "\n" +#define vcipher_(vt, va, vb) "\tvcipher\t" #vt "," #va "," #vb "\n" +#define vcipherlast_(vt, va, vb) "\tvcipherlast\t" #vt "," #va "," #vb "\n" +#define vncipher_(vt, va, vb) "\tvncipher\t" #vt "," #va "," #vb "\n" +#define vncipherlast_(vt, va, vb) "\tvncipherlast\t" #vt "," #va "," #vb "\n" +#define vperm_(vt, va, vb, vc) "\tvperm\t" #vt "," #va "," #vb "," #vc "\n" +#define vpmsumd_(vt, va, vb) "\tvpmsumd\t" #vt "," #va "," #vb "\n" +#define xxpermdi_(vt, va, vb, d) "\txxpermdi\t" #vt "," #va "," #vb "," #d "\n" + +#endif + +/* ==================================================================== */ + #endif