1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-04-01 11:12:50 -05:00
putty-source/crypto/CMakeLists.txt
Simon Tatham 965057d6d6 Change strategy for the Arm instruction setting DIT.
Colin Watson reported that a build failure occurred in the AArch64
Debian build of PuTTY 0.83:

gcc now defaults to enabling branch protection using AArch64 pointer
authentication, if the target architecture version supports it.
Debian's base supported architecture does not, but Armv8.4-A does. So
when I changed the compile flags for enable_dit.c to add
-march=armv8.4-a, it didn't _just_ allow me to write the 'msr dit, %0'
instruction in my asm statement; it also unexpectedly turned on
pointer authentication in the containing function, which caused a
SIGILL when running on a pre-Armv8.4-A CPU, because although the code
correctly skipped the instruction that set DIT, it was already inside
enable_dit() at that point and couldn't avoid going through the
unsupported 'retaa' instruction which tries to check an auth code on
the return address.

An obvious approach would be to add -mbranch-protection=none to the
compile flags for enable_dit.c. Another approach is to leave the
_compiler_ flags alone, and change the architecture in the assembler,
either via a fiddly -Wa,... option or by putting a .arch directive
inside the asm statement. But both have downsides. Turning off branch
protection is fine for the Debian build, but has the unwanted side
effect of turning it off (in that one function) even in builds
targeting a later architecture which _did_ want branch protection. And
changing the assembler's architecture risks changing it _down_ instead
of up, again perhaps invalidating other instructions generated by the
compiler (like if some later security feature is introduced that gcc
also wants to turn on by default).

So instead I've taken the much simpler approach of not bothering to
change the target architecture at all, and instead generating the move
into DIT by hardcoding its actual instruction encoding. This meant I
also had to force the input value into a specific register, but I
don't think that does any harm (not _even_ wasting an extra
instruction in codegen). Now we should avoid interfering with any
security features the compiler wants to turn on or off: all of that
should be independent of the instruction I really wanted.
2025-02-15 15:57:53 +00:00

260 lines
7.7 KiB
CMake

add_sources_from_current_dir(crypto
aes-common.c
aes-select.c
aes-sw.c
aesgcm-common.c
aesgcm-select.c
aesgcm-sw.c
aesgcm-ref-poly.c
arcfour.c
argon2.c
bcrypt.c
blake2.c
blowfish.c
chacha20-poly1305.c
crc32.c
des.c
diffie-hellman.c
dsa.c
ecc-arithmetic.c
ecc-ssh.c
hash_simple.c
hmac.c
kex-hybrid.c
mac.c
mac_simple.c
md5.c
mlkem.c
mpint.c
ntru.c
openssh-certs.c
prng.c
pubkey-pem.c
pubkey-ppk.c
pubkey-ssh1.c
rfc6979.c
rsa.c
sha256-common.c
sha256-select.c
sha256-sw.c
sha512-common.c
sha512-select.c
sha512-sw.c
sha3.c
sha1-common.c
sha1-select.c
sha1-sw.c
xdmauth.c)
include(CheckCSourceCompiles)
function(test_compile_with_flags outvar)
cmake_parse_arguments(OPT "" ""
"GNU_FLAGS;MSVC_FLAGS;ADD_SOURCES_IF_SUCCESSFUL;TEST_SOURCE" "${ARGN}")
# Figure out what flags are applicable to this compiler.
set(flags)
if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR
CMAKE_C_COMPILER_ID MATCHES "Clang")
set(flags ${OPT_GNU_FLAGS})
endif()
if(CMAKE_C_COMPILER_ID MATCHES "MSVC")
set(flags ${OPT_MSVC_FLAGS})
endif()
# See if we can compile the provided test program.
foreach(i ${flags})
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${i}")
endforeach()
check_c_source_compiles("${OPT_TEST_SOURCE}" "${outvar}")
if(${outvar} AND OPT_ADD_SOURCES_IF_SUCCESSFUL)
# Make an object library that compiles the implementation with the
# necessary flags, and add the resulting objects to the crypto
# library.
set(libname object_lib_${outvar})
add_library(${libname} OBJECT ${OPT_ADD_SOURCES_IF_SUCCESSFUL})
target_compile_options(${libname} PRIVATE ${flags})
target_sources(crypto PRIVATE $<TARGET_OBJECTS:${libname}>)
endif()
# Export the output to the caller's scope, so that further tests can
# be based on it.
set(${outvar} ${${outvar}} PARENT_SCOPE)
endfunction()
# ----------------------------------------------------------------------
# Try to enable x86 intrinsics-based crypto implementations.
test_compile_with_flags(HAVE_WMMINTRIN_H
GNU_FLAGS -msse4.1
TEST_SOURCE "
#include <wmmintrin.h>
#include <smmintrin.h>
volatile __m128i r, a, b;
int main(void) { r = _mm_xor_si128(a, b); }")
if(HAVE_WMMINTRIN_H)
test_compile_with_flags(HAVE_AES_NI
GNU_FLAGS -msse4.1 -maes
TEST_SOURCE "
#include <wmmintrin.h>
#include <smmintrin.h>
volatile __m128i r, a, b;
int main(void) { r = _mm_aesenc_si128(a, b); }"
ADD_SOURCES_IF_SUCCESSFUL aes-ni aes-ni.c)
# shaintrin.h doesn't exist on all compilers; sometimes it's folded
# into the other headers
test_compile_with_flags(HAVE_SHAINTRIN_H
GNU_FLAGS -msse4.1 -msha
TEST_SOURCE "
#include <wmmintrin.h>
#include <smmintrin.h>
#include <immintrin.h>
#include <shaintrin.h>
volatile __m128i r, a, b;
int main(void) { r = _mm_xor_si128(a, b); }")
if(HAVE_SHAINTRIN_H)
set(include_shaintrin "#include <shaintrin.h>")
else()
set(include_shaintrin "")
endif()
test_compile_with_flags(HAVE_SHA_NI
GNU_FLAGS -msse4.1 -msha
TEST_SOURCE "
#include <wmmintrin.h>
#include <smmintrin.h>
#include <immintrin.h>
${include_shaintrin}
volatile __m128i r, a, b, c;
int main(void) { r = _mm_sha256rnds2_epu32(a, b, c); }"
ADD_SOURCES_IF_SUCCESSFUL sha256-ni.c sha1-ni.c)
test_compile_with_flags(HAVE_CLMUL
GNU_FLAGS -msse4.1 -mpclmul
TEST_SOURCE "
#include <wmmintrin.h>
#include <tmmintrin.h>
volatile __m128i r, a, b;
int main(void) { r = _mm_clmulepi64_si128(a, b, 5);
r = _mm_shuffle_epi8(r, a); }"
ADD_SOURCES_IF_SUCCESSFUL aesgcm-clmul.c)
endif()
# ----------------------------------------------------------------------
# Try to enable Arm Neon intrinsics-based crypto implementations.
# Start by checking which header file we need. ACLE specifies that it
# ought to be <arm_neon.h>, on both 32- and 64-bit Arm, but Visual
# Studio for some reason renamed the header to <arm64_neon.h> in
# 64-bit, and gives an error if you use the standard name. (However,
# clang-cl does let you use the standard name.)
test_compile_with_flags(HAVE_ARM_NEON_H
MSVC_FLAGS -D_ARM_USE_NEW_NEON_INTRINSICS
TEST_SOURCE "
#include <arm_neon.h>
volatile uint8x16_t r, a, b;
int main(void) { r = veorq_u8(a, b); }")
if(HAVE_ARM_NEON_H)
set(neon ON)
set(neon_header "arm_neon.h")
else()
test_compile_with_flags(HAVE_ARM64_NEON_H TEST_SOURCE "
#include <arm64_neon.h>
volatile uint8x16_t r, a, b;
int main(void) { r = veorq_u8(a, b); }")
if(HAVE_ARM64_NEON_H)
set(neon ON)
set(neon_header "arm64_neon.h")
set(USE_ARM64_NEON_H ON)
endif()
endif()
if(neon)
# If we have _some_ NEON header, look for the individual things we
# can enable with it.
# The 'crypto' architecture extension includes support for AES,
# SHA-1, and SHA-256.
test_compile_with_flags(HAVE_NEON_CRYPTO
GNU_FLAGS -march=armv8-a+crypto
MSVC_FLAGS -D_ARM_USE_NEW_NEON_INTRINSICS
TEST_SOURCE "
#include <${neon_header}>
volatile uint8x16_t r, a, b;
volatile uint32x4_t s, x, y, z;
int main(void) { r = vaeseq_u8(a, b); s = vsha256hq_u32(x, y, z); }"
ADD_SOURCES_IF_SUCCESSFUL aes-neon.c sha256-neon.c sha1-neon.c)
test_compile_with_flags(HAVE_NEON_PMULL
GNU_FLAGS -march=armv8-a+crypto
MSVC_FLAGS -D_ARM_USE_NEW_NEON_INTRINSICS
TEST_SOURCE "
#include <${neon_header}>
volatile poly128_t r;
volatile poly64_t a, b;
volatile poly64x2_t u, v;
int main(void) { r = vmull_p64(a, b); r = vmull_high_p64(u, v); }"
ADD_SOURCES_IF_SUCCESSFUL aesgcm-neon.c)
test_compile_with_flags(HAVE_NEON_VADDQ_P128
GNU_FLAGS -march=armv8-a+crypto
MSVC_FLAGS -D_ARM_USE_NEW_NEON_INTRINSICS
TEST_SOURCE "
#include <${neon_header}>
volatile poly128_t r;
int main(void) { r = vaddq_p128(r, r); }")
# The 'sha3' architecture extension, despite the name, includes
# support for SHA-512 (from the SHA-2 standard) as well as SHA-3
# proper.
#
# Versions of clang up to and including clang 12 support this
# extension in assembly language, but not the ACLE intrinsics for
# it. So we check both.
test_compile_with_flags(HAVE_NEON_SHA512_INTRINSICS
GNU_FLAGS -march=armv8.2-a+crypto+sha3
TEST_SOURCE "
#include <${neon_header}>
volatile uint64x2_t r, a, b;
int main(void) { r = vsha512su0q_u64(a, b); }"
ADD_SOURCES_IF_SUCCESSFUL sha512-neon.c)
if(HAVE_NEON_SHA512_INTRINSICS)
set(HAVE_NEON_SHA512 ON)
else()
test_compile_with_flags(HAVE_NEON_SHA512_ASM
GNU_FLAGS -march=armv8.2-a+crypto+sha3
TEST_SOURCE "
#include <${neon_header}>
volatile uint64x2_t r, a;
int main(void) { __asm__(\"sha512su0 %0.2D,%1.2D\" : \"+w\" (r) : \"w\" (a)); }"
ADD_SOURCES_IF_SUCCESSFUL sha512-neon.c)
if(HAVE_NEON_SHA512_ASM)
set(HAVE_NEON_SHA512 ON)
endif()
endif()
endif()
test_compile_with_flags(HAVE_ARM_DIT
TEST_SOURCE "
#ifndef __aarch64__
#error make sure this only even tries to work on AArch64
#endif
#include <stdint.h>
int main(void) {
register uint64_t one asm(\"x8\");
one = 1;
asm volatile(\".inst 0xd51b42a8\" :: \"r\"(one));
}"
ADD_SOURCES_IF_SUCCESSFUL enable_dit.c)
set(HAVE_AES_NI ${HAVE_AES_NI} PARENT_SCOPE)
set(HAVE_SHA_NI ${HAVE_SHA_NI} PARENT_SCOPE)
set(HAVE_SHAINTRIN_H ${HAVE_SHAINTRIN_H} PARENT_SCOPE)
set(HAVE_NEON_CRYPTO ${HAVE_NEON_CRYPTO} PARENT_SCOPE)
set(HAVE_NEON_SHA512 ${HAVE_NEON_SHA512} PARENT_SCOPE)
set(HAVE_NEON_SHA512_INTRINSICS ${HAVE_NEON_SHA512_INTRINSICS} PARENT_SCOPE)
set(USE_ARM64_NEON_H ${USE_ARM64_NEON_H} PARENT_SCOPE)
set(HAVE_ARM_DIT ${HAVE_ARM_DIT} PARENT_SCOPE)