Skip to content

Commit d3eac45

Browse files
TimWollacmb69
andcommitted
hash: Add SHA-NI implementation of SHA-256
Implementation taken from Tarsnap/libcperciva@661752a. Co-authored-by: Christoph M. Becker <cmbecker69@gmx.de>
1 parent 752db5d commit d3eac45

File tree

6 files changed

+197
-3
lines changed

6 files changed

+197
-3
lines changed

Diff for: README.REDIST.BINS

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
18. avifinfo (ext/standard/libavifinfo) see ext/standard/libavifinfo/LICENSE
1919
19. xxHash (ext/hash/xxhash)
2020
20. Lexbor (ext/dom/lexbor/lexbor) see ext/dom/lexbor/LICENSE
21-
21. Portions of libcperciva (ext/hash/hash_sha_sse2.c) see the header in the source file
21+
21. Portions of libcperciva (ext/hash/hash_sha_{ni,sse2}.c) see the header in the source file
2222

2323
3. pcre2lib (ext/pcre)
2424

Diff for: ext/hash/config.m4

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ else
3333
PHP_HASH_CFLAGS="$PHP_HASH_CFLAGS -I@ext_srcdir@/$SHA3_DIR -DKeccakP200_excluded -DKeccakP400_excluded -DKeccakP800_excluded -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"
3434
fi
3535

36-
EXT_HASH_SOURCES="hash.c hash_md.c hash_sha.c hash_sha_sse2.c hash_ripemd.c hash_haval.c \
36+
EXT_HASH_SOURCES="hash.c hash_md.c hash_sha.c hash_sha_sse2.c hash_sha_ni.c hash_ripemd.c hash_haval.c \
3737
hash_tiger.c hash_gost.c hash_snefru.c hash_whirlpool.c hash_adler32.c \
3838
hash_crc32.c hash_fnv.c hash_joaat.c $EXT_HASH_SHA3_SOURCES
3939
murmur/PMurHash.c murmur/PMurHash128.c hash_murmur.c hash_xxhash.c"

Diff for: ext/hash/config.w32

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ if (PHP_MHASH != 'no') {
99

1010
PHP_HASH = 'yes';
1111

12-
EXTENSION('hash', 'hash.c hash_md.c hash_sha.c hash_sha_sse2.c hash_ripemd.c hash_haval.c ' +
12+
EXTENSION('hash', 'hash.c hash_md.c hash_sha.c hash_sha_sse2.c hash_sha_ni.c hash_ripemd.c hash_haval.c ' +
1313
'hash_tiger.c hash_gost.c hash_snefru.c hash_whirlpool.c ' +
1414
'hash_adler32.c hash_crc32.c hash_joaat.c hash_fnv.c ' +
1515
'hash_sha3.c hash_murmur.c hash_xxhash.c', false);

Diff for: ext/hash/hash_sha.c

+11
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "php_hash.h"
1919
#include "php_hash_sha.h"
20+
#include "Zend/zend_cpuinfo.h"
2021

2122
static const unsigned char PADDING[128] =
2223
{
@@ -160,6 +161,16 @@ PHP_HASH_API void PHP_SHA256InitArgs(PHP_SHA256_CTX * context, ZEND_ATTRIBUTE_UN
160161
*/
161162
static void SHA256Transform(uint32_t state[8], const unsigned char block[64])
162163
{
164+
#if defined(ZEND_INTRIN_SHA_NATIVE)
165+
SHA256_Transform_shani(state, block);
166+
return;
167+
#elif defined(ZEND_INTRIN_SHA_RESOLVER)
168+
if (zend_cpu_supports(ZEND_CPU_FEATURE_SSSE3) && zend_cpu_supports(ZEND_CPU_FEATURE_SHA)) {
169+
SHA256_Transform_shani(state, block);
170+
return;
171+
}
172+
#endif
173+
163174
#if defined(__SSE2__)
164175
uint32_t tmp32[72];
165176

Diff for: ext/hash/hash_sha_ni.c

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/*-
2+
* Copyright 2018 Tarsnap Backup Inc.
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions
7+
* are met:
8+
* 1. Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* 2. Redistributions in binary form must reproduce the above copyright
11+
* notice, this list of conditions and the following disclaimer in the
12+
* documentation and/or other materials provided with the distribution.
13+
*
14+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17+
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20+
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23+
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24+
* SUCH DAMAGE.
25+
*/
26+
27+
#include "php_hash.h"
28+
#include "php_hash_sha.h"
29+
30+
#if (defined(__i386__) || defined(__x86_64__)) && defined(HAVE_IMMINTRIN_H)
31+
32+
# include <immintrin.h>
33+
34+
# if ZEND_INTRIN_SHA_RESOLVER
35+
static __m128i be32dec_128(const uint8_t * src) __attribute__((target("ssse3")));
36+
void SHA256_Transform_shani(uint32_t state[static restrict 8], const uint8_t block[static restrict 64]) __attribute__((target("ssse3,sha")));
37+
# endif
38+
39+
/* Original implementation from libcperciva follows. */
40+
41+
/**
42+
* This code uses intrinsics from the following feature sets:
43+
* SHANI: _mm_sha256msg1_epu32, _mm_sha256msg2_epu32, _mm_sha256rnds2_epu32
44+
* SSSE3: _mm_shuffle_epi8, _mm_alignr_epi8
45+
* SSE2: Everything else
46+
*
47+
* The SSSE3 intrinsics could be avoided at a slight cost by using a few SSE2
48+
* instructions in their place; we have not done this since to our knowledge
49+
* there are presently no CPUs which support the SHANI instruction set but do
50+
* not support SSSE3.
51+
*/
52+
53+
/* Load 32-bit big-endian words. */
54+
static __m128i
55+
be32dec_128(const uint8_t * src)
56+
{
57+
const __m128i SHUF = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11,
58+
4, 5, 6, 7, 0, 1, 2, 3);
59+
__m128i x;
60+
61+
/* Load four 32-bit words. */
62+
x = _mm_loadu_si128((const __m128i *)src);
63+
64+
/* Reverse the order of the bytes in each word. */
65+
return (_mm_shuffle_epi8(x, SHUF));
66+
}
67+
68+
/* Convert an unsigned 32-bit immediate into a signed value. */
69+
#define I32(a) ((UINT32_C(a) >= UINT32_C(0x80000000)) ? \
70+
-(int32_t)(UINT32_C(0xffffffff) - UINT32_C(a)) - 1 : (int32_t)INT32_C(a))
71+
72+
/* Load four unsigned 32-bit immediates into a vector register. */
73+
#define IMM4(a, b, c, d) _mm_set_epi32(I32(a), I32(b), I32(c), I32(d))
74+
75+
/* Run four rounds of SHA256. */
76+
#define RND4(S, W, K0, K1, K2, K3) do { \
77+
__m128i M; \
78+
\
79+
/* Add the next four words of message schedule and round constants. */ \
80+
M = _mm_add_epi32(W, IMM4(K3, K2, K1, K0)); \
81+
\
82+
/* Perform two rounds of SHA256, using the low two words in M. */ \
83+
S[1] = _mm_sha256rnds2_epu32(S[1], S[0], M); \
84+
\
85+
/* Shift the two words of M down and perform the next two rounds. */ \
86+
M = _mm_srli_si128(M, 8); \
87+
S[0] = _mm_sha256rnds2_epu32(S[0], S[1], M); \
88+
} while (0)
89+
90+
/* Compute the ith set of four words of message schedule. */
91+
#define MSG4(W, i) do { \
92+
W[(i + 0) % 4] = _mm_sha256msg1_epu32(W[(i + 0) % 4], W[(i + 1) % 4]); \
93+
W[(i + 0) % 4] = _mm_add_epi32(W[(i + 0) % 4], \
94+
_mm_alignr_epi8(W[(i + 3) % 4], W[(i + 2) % 4], 4)); \
95+
W[(i + 0) % 4] = _mm_sha256msg2_epu32(W[(i + 0) % 4], W[(i + 3) % 4]); \
96+
} while (0)
97+
98+
/* Perform 4 rounds of SHA256 and generate more message schedule if needed. */
99+
#define RNDMSG(S, W, i, K0, K1, K2, K3) do { \
100+
RND4(S, W[i % 4], K0, K1, K2, K3); \
101+
if (i < 12) \
102+
MSG4(W, i + 4); \
103+
} while (0)
104+
105+
/**
106+
* SHA256_Transform_shani(state, block):
107+
* Compute the SHA256 block compression function, transforming ${state} using
108+
* the data in ${block}. This implementation uses x86 SHANI and SSSE3
109+
* instructions, and should only be used if CPUSUPPORT_X86_SHANI and _SSSE3
110+
* are defined and cpusupport_x86_shani() and _ssse3() return nonzero.
111+
*/
112+
void
113+
SHA256_Transform_shani(uint32_t state[static restrict 8],
114+
const uint8_t block[static restrict 64])
115+
{
116+
__m128i S3210, S7654;
117+
__m128i S0123, S4567;
118+
__m128i S0145, S2367;
119+
__m128i W[4];
120+
__m128i S[2];
121+
122+
/* Load state. */
123+
S3210 = _mm_loadu_si128((const __m128i *)&state[0]);
124+
S7654 = _mm_loadu_si128((const __m128i *)&state[4]);
125+
126+
/* Shuffle the 8 32-bit values into the order we need them. */
127+
S0123 = _mm_shuffle_epi32(S3210, 0x1B);
128+
S4567 = _mm_shuffle_epi32(S7654, 0x1B);
129+
S0145 = _mm_unpackhi_epi64(S4567, S0123);
130+
S2367 = _mm_unpacklo_epi64(S4567, S0123);
131+
132+
/* Load input block; this is the start of the message schedule. */
133+
W[0] = be32dec_128(&block[0]);
134+
W[1] = be32dec_128(&block[16]);
135+
W[2] = be32dec_128(&block[32]);
136+
W[3] = be32dec_128(&block[48]);
137+
138+
/* Initialize working variables. */
139+
S[0] = S0145;
140+
S[1] = S2367;
141+
142+
/* Perform 64 rounds, 4 at a time. */
143+
RNDMSG(S, W, 0, 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5);
144+
RNDMSG(S, W, 1, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5);
145+
RNDMSG(S, W, 2, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3);
146+
RNDMSG(S, W, 3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174);
147+
RNDMSG(S, W, 4, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc);
148+
RNDMSG(S, W, 5, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da);
149+
RNDMSG(S, W, 6, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7);
150+
RNDMSG(S, W, 7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967);
151+
RNDMSG(S, W, 8, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13);
152+
RNDMSG(S, W, 9, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85);
153+
RNDMSG(S, W, 10, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3);
154+
RNDMSG(S, W, 11, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070);
155+
RNDMSG(S, W, 12, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5);
156+
RNDMSG(S, W, 13, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3);
157+
RNDMSG(S, W, 14, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208);
158+
RNDMSG(S, W, 15, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2);
159+
160+
/* Mix local working variables into global state. */
161+
S0145 = _mm_add_epi32(S0145, S[0]);
162+
S2367 = _mm_add_epi32(S2367, S[1]);
163+
164+
/* Shuffle state back to the original word order and store. */
165+
S0123 = _mm_unpackhi_epi64(S2367, S0145);
166+
S4567 = _mm_unpacklo_epi64(S2367, S0145);
167+
S3210 = _mm_shuffle_epi32(S0123, 0x1B);
168+
S7654 = _mm_shuffle_epi32(S4567, 0x1B);
169+
_mm_storeu_si128((__m128i *)&state[0], S3210);
170+
_mm_storeu_si128((__m128i *)&state[4], S7654);
171+
}
172+
173+
#endif

Diff for: ext/hash/php_hash_sha.h

+10
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ PHP_HASH_API void PHP_SHA256Update(PHP_SHA256_CTX *, const unsigned char *, size
5656
void SHA256_Transform_sse2(uint32_t state[PHP_STATIC_RESTRICT 8], const uint8_t block[PHP_STATIC_RESTRICT 64], uint32_t W[PHP_STATIC_RESTRICT 64], uint32_t S[PHP_STATIC_RESTRICT 8]);
5757
#endif
5858

59+
#if (defined(__i386__) || defined(__x86_64__)) && defined(HAVE_IMMINTRIN_H)
60+
# if defined(__SSSE3__) && defined(__SHA__)
61+
# define ZEND_INTRIN_SHA_NATIVE 1
62+
# elif defined(HAVE_FUNC_ATTRIBUTE_TARGET)
63+
# define ZEND_INTRIN_SHA_RESOLVER 1
64+
# endif
65+
66+
void SHA256_Transform_shani(uint32_t state[PHP_STATIC_RESTRICT 8], const uint8_t block[PHP_STATIC_RESTRICT 64]);
67+
#endif
68+
5969
PHP_HASH_API void PHP_SHA256Final(unsigned char[32], PHP_SHA256_CTX *);
6070

6171
/* SHA384 context */

0 commit comments

Comments
 (0)