diff options
-rw-r--r-- | src/Crypto/cpu.c | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/src/Crypto/cpu.c b/src/Crypto/cpu.c index 174596eb..c00d8409 100644 --- a/src/Crypto/cpu.c +++ b/src/Crypto/cpu.c @@ -28,60 +28,70 @@ int CpuId(uint32 input, uint32 output[4]) __cpuid((int *)output, input); return 1; } #else #ifndef CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY #if defined(__cplusplus) extern "C" { #endif typedef void (*SigHandler)(int); static jmp_buf s_jmpNoCPUID; static void SigIllHandlerCPUID(int p) { longjmp(s_jmpNoCPUID, 1); } #if !defined (_UEFI) && ((defined(__AES__) && defined(__PCLMUL__)) || defined(__INTEL_COMPILER) || CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE) static jmp_buf s_jmpNoAESNI; static void SigIllHandlerAESNI(int p) { longjmp(s_jmpNoAESNI, 1); } #endif +#if !defined (_UEFI) && (defined(__SHA__) || defined(__INTEL_COMPILER) || CRYPTOPP_SHANI_AVAILABLE) + +static jmp_buf s_jmpNoSHA; +static void SigIllHandlerSHA(int p) +{ + longjmp(s_jmpNoSHA, 1); +} + +#endif + #if CRYPTOPP_BOOL_X64 == 0 static jmp_buf s_jmpNoSSE2; static void SigIllHandlerSSE2(int p) { longjmp(s_jmpNoSSE2, 1); } #endif #if defined(__cplusplus) } #endif #endif int CpuId(uint32 input, uint32 output[4]) { #ifdef CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY #ifndef _UEFI __try { #endif __asm { mov eax, input mov ecx, 0 cpuid mov edi, output mov [edi], eax mov [edi+4], ebx mov [edi+8], ecx mov [edi+12], edx @@ -284,60 +294,109 @@ static int Detect_MS_HyperV_AES () int hasAesNI = 0; // when Hyper-V is enabled on older versions of Windows Server (i.e. 2008 R2), the AES-NI capability // gets masked out for all applications, even running on the host. // We try to detect Hyper-V virtual CPU and perform a dummy AES-NI operation to check its real presence uint32 cpuid[4] = {0}; char HvProductName[13]; CpuId(0x40000000, cpuid); memcpy (HvProductName, &cpuid[1], 12); HvProductName[12] = 0; if (_stricmp(HvProductName, "Microsoft Hv") == 0) { #if defined (TC_WINDOWS_DRIVER) && !defined (_WIN64) KFLOATING_SAVE floatingPointState; if (NT_SUCCESS (KeSaveFloatingPointState (&floatingPointState))) { #endif hasAesNI = TryAESNI (); #if defined (TC_WINDOWS_DRIVER) && !defined (_WIN64) KeRestoreFloatingPointState (&floatingPointState); } #endif } return hasAesNI; } #endif +#if defined(__SHA__) || defined(__INTEL_COMPILER) || CRYPTOPP_SHANI_AVAILABLE +static int TrySHA256() +{ + volatile int result = 0; +#ifdef _MSC_VER + __try +#else + SigHandler oldHandler = signal(SIGILL, SigIllHandlerSHA); + if (oldHandler == SIG_ERR) + return 0; + if (setjmp(s_jmpNoSHA)) + result = 0; + else +#endif + { + // Known input message block + __m128i msg0 = _mm_setr_epi32(0x12345678, 0x9ABCDEF0, 0x87654321, 0x0FEDCBA9); + __m128i msg1 = _mm_setr_epi32(0x11111111, 0x22222222, 0x33333333, 0x44444444); + + // SHA256 message schedule update + __m128i tmp = _mm_sha256msg1_epu32(msg0, msg1); + + // Verify result - these values were pre-computed for the given input +#ifdef _MSC_VER + if (tmp.m128i_u32[0] == 0xD8131B44 && + tmp.m128i_u32[1] == 0x9DE6E22B && + tmp.m128i_u32[2] == 0xA86D643A && + tmp.m128i_u32[3] == 0x74320FED) +#else + if (((uint32_t*)(&tmp))[0] == 0xD8131B44 && + ((uint32_t*)(&tmp))[1] == 0x9DE6E22B && + ((uint32_t*)(&tmp))[2] == 0xA86D643A && + ((uint32_t*)(&tmp))[3] == 0x74320FED) +#endif + result = 1; + } +#ifdef _MSC_VER + __except (EXCEPTION_EXECUTE_HANDLER) + { + // ignore error if SHA instructions not supported + } +#else + signal(SIGILL, oldHandler); +#endif + + return result; +} +#endif + static BOOL CheckSHA256Support() { #if CRYPTOPP_BOOL_X64 && CRYPTOPP_SHANI_AVAILABLE #if defined(_MSC_VER) // Windows with MSVC int cpuInfo[4] = { 0 }; __cpuidex(cpuInfo, 7, 0); return (cpuInfo[1] & (1 << 29)) != 0? TRUE : FALSE; #elif defined(__GNUC__) || defined(__clang__) // Linux, FreeBSD, macOS with GCC/Clang unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; // First check if CPUID leaf 7 is supported if (__get_cpuid(0, &eax, &ebx, &ecx, &edx)) { if (eax >= 7) { // Now check SHA-256 support in leaf 7, sub-leaf 0 if (__get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx)) { return (ebx & (1 << 29)) != 0? TRUE : FALSE; } } } return FALSE; #else #error "Unsupported compiler" #endif #else return FALSE; #endif } void DetectX86Features() @@ -352,60 +411,67 @@ void DetectX86Features() // cpuid1[2] & (1 << 27) is XSAVE/XRESTORE and signals OS support for SSE; use it to avoid probes. // See http://github.com/weidai11/cryptopp/issues/511 and http://stackoverflow.com/a/22521619/608639 if ((cpuid1[3] & (1 << 26)) != 0) g_hasSSE2 = (cpuid1[2] & (1 << 27)) || TrySSE2(); if (g_hasSSE2 && (cpuid1[2] & (1 << 28)) && (cpuid1[2] & (1 << 27)) && (cpuid1[2] & (1 << 26))) /* CPU has AVX and OS supports XSAVE/XRSTORE */ { uint64 xcrFeatureMask = xgetbv(); g_hasAVX = (xcrFeatureMask & 0x6) == 0x6; } g_hasAVX2 = g_hasAVX && (cpuid1[1] & (1 << 5)); g_hasBMI2 = g_hasSSE2 && (cpuid1[1] & (1 << 8)); g_hasSSE42 = g_hasSSE2 && (cpuid1[2] & (1 << 20)); g_hasSSE41 = g_hasSSE2 && (cpuid1[2] & (1 << 19)); g_hasSSSE3 = g_hasSSE2 && (cpuid1[2] & (1<<9)); #ifndef CRYPTOPP_DISABLE_AESNI g_hasAESNI = g_hasSSE2 && (cpuid1[2] & (1<<25)); #endif g_hasCLMUL = g_hasSSE2 && (cpuid1[2] & (1<<1)); g_hasSHA256 = CheckSHA256Support(); #if !defined (_UEFI) && ((defined(__AES__) && defined(__PCLMUL__)) || defined(__INTEL_COMPILER) || CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE) // Hypervisor = bit 31 of ECX of CPUID leaf 0x1 // reference: http://artemonsecurity.com/vmde.pdf if (!g_hasAESNI && (cpuid1[2] & (1<<31))) { g_hasAESNI = Detect_MS_HyperV_AES (); } #endif +#if defined(__SHA__) || defined(__INTEL_COMPILER) || CRYPTOPP_SHANI_AVAILABLE + if (!g_hasSHA256) + { + g_hasSHA256 = TrySHA256(); + } +#endif + if ((cpuid1[3] & (1 << 25)) != 0) g_hasISSE = 1; else { CpuId(0x080000000, cpuid2); if (cpuid2[0] >= 0x080000001) { CpuId(0x080000001, cpuid2); g_hasISSE = (cpuid2[3] & (1 << 22)) != 0; } } if (IsIntel(cpuid)) { g_isIntel = 1; g_isP4 = ((cpuid1[0] >> 8) & 0xf) == 0xf; g_cacheLineSize = 8 * GETBYTE(cpuid1[1], 1); g_hasRDRAND = (cpuid1[2] & (1 << 30)) != 0; if (cpuid[0] >= 7) { if (CpuId(7, cpuid2)) { g_hasRDSEED = (cpuid2[1] & (1 << 18)) != 0; g_hasAVX2 = (cpuid2[1] & (1 << 5)) != 0; g_hasBMI2 = (cpuid2[1] & (1 << 8)) != 0; } } } else if (IsAMD(cpuid) || IsHygon(cpuid)) |