xref: /PHP-8.2/ext/hash/hash_sha3.c (revision 01b3fc03)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Author: Sara Golemon <pollita@php.net>                               |
14    +----------------------------------------------------------------------+
15 */
16 
17 #include "php_hash.h"
18 #include "php_hash_sha3.h"
19 
20 #ifdef HAVE_SLOW_HASH3
21 // ================= slow algo ==============================================
22 
23 #if (defined(__APPLE__) || defined(__APPLE_CC__)) && \
24     (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__))
25 # if defined(__LITTLE_ENDIAN__)
26 #  undef WORDS_BIGENDIAN
27 # else
28 #  if defined(__BIG_ENDIAN__)
29 #   define WORDS_BIGENDIAN
30 #  endif
31 # endif
32 #endif
33 
rol64(uint64_t v,unsigned char b)34 static inline uint64_t rol64(uint64_t v, unsigned char b) {
35 	return (v << b) | (v >> (64 - b));
36 }
idx(unsigned char x,unsigned char y)37 static inline unsigned char idx(unsigned char x, unsigned char y) {
38 	return x + (5 * y);
39 }
40 
41 #ifdef WORDS_BIGENDIAN
load64(const unsigned char * x)42 static inline uint64_t load64(const unsigned char* x) {
43 	signed char i;
44 	uint64_t ret = 0;
45 	for (i = 7; i >= 0; --i) {
46 		ret <<= 8;
47 		ret |= x[i];
48 	}
49 	return ret;
50 }
store64(unsigned char * x,uint64_t val)51 static inline void store64(unsigned char* x, uint64_t val) {
52 	size_t i;
53 	for (i = 0; i < 8; ++i) {
54 		x[i] = val & 0xFF;
55 		val >>= 8;
56 	}
57 }
xor64(unsigned char * x,uint64_t val)58 static inline void xor64(unsigned char* x, uint64_t val) {
59 	size_t i;
60 	for (i = 0; i < 8; ++i) {
61 		x[i] ^= val & 0xFF;
62 		val >>= 8;
63 	}
64 }
65 # define readLane(x, y)     load64(ctx->state+sizeof(uint64_t)*idx(x, y))
66 # define writeLane(x, y, v) store64(ctx->state+sizeof(uint64_t)*idx(x, y), v)
67 # define XORLane(x, y, v)   xor64(ctx->state+sizeof(uint64_t)*idx(x, y), v)
68 #else
69 # define readLane(x, y)     (((uint64_t*)ctx->state)[idx(x,y)])
70 # define writeLane(x, y, v) (((uint64_t*)ctx->state)[idx(x,y)] = v)
71 # define XORLane(x, y, v)   (((uint64_t*)ctx->state)[idx(x,y)] ^= v)
72 #endif
73 
LFSR86540(unsigned char * pLFSR)74 static inline char LFSR86540(unsigned char* pLFSR)
75 {
76 	unsigned char LFSR = *pLFSR;
77 	char result = LFSR & 0x01;
78 	if (LFSR & 0x80) {
79 		// Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1
80 		LFSR = (LFSR << 1) ^ 0x71;
81 	} else {
82 		LFSR <<= 1;
83 	}
84 	*pLFSR = LFSR;
85 	return result;
86 }
87 
permute(PHP_SHA3_CTX * ctx)88 static void permute(PHP_SHA3_CTX* ctx) {
89 	unsigned char LFSRstate = 0x01;
90 	unsigned char round;
91 
92 	for (round = 0; round < 24; ++round) {
93 		{ // Theta step (see [Keccak Reference, Section 2.3.2])
94 			uint64_t C[5], D;
95 			unsigned char x, y;
96 			for (x = 0; x < 5; ++x) {
97 				C[x] = readLane(x, 0) ^ readLane(x, 1) ^
98 				readLane(x, 2) ^ readLane(x, 3) ^ readLane(x, 4);
99 			}
100 			for (x = 0; x < 5; ++x) {
101 				D = C[(x+4)%5] ^ rol64(C[(x+1)%5], 1);
102 				for (y = 0; y < 5; ++y) {
103 					XORLane(x, y, D);
104 				}
105 			}
106 		}
107 
108 		{ // p and Pi steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4])
109 			unsigned char x = 1, y = 0, t;
110 			uint64_t current = readLane(x, y);
111 			for (t = 0; t < 24; ++t) {
112 				unsigned char r = ((t + 1) * (t + 2) / 2) % 64;
113 				unsigned char Y = (2*x + 3*y) % 5;
114 				uint64_t temp;
115 				x = y;
116 				y = Y;
117 				temp = readLane(x, y);
118 				writeLane(x, y, rol64(current, r));
119 				current = temp;
120 			}
121 		}
122 
123 		{ // X step (see [Keccak Reference, Section 2.3.1])
124 			unsigned char x, y;
125 			for (y = 0; y < 5; ++y) {
126 				uint64_t temp[5];
127 				for (x = 0; x < 5; ++x) {
128 					temp[x] = readLane(x, y);
129 				}
130 				for (x = 0; x < 5; ++x) {
131 					writeLane(x, y, temp[x] ^((~temp[(x+1)%5]) & temp[(x+2)%5]));
132 				}
133 			}
134 		}
135 
136 		{ // i step (see [Keccak Reference, Section 2.3.5])
137 			unsigned char j;
138 			for (j = 0; j < 7; ++j) {
139 				if (LFSR86540(&LFSRstate)) {
140 					uint64_t bitPos = (1<<j) - 1;
141 					XORLane(0, 0, (uint64_t)1 << bitPos);
142 				}
143 			}
144 		}
145 	}
146 }
147 
148 // ==========================================================================
149 
PHP_SHA3_Init(PHP_SHA3_CTX * ctx,int bits)150 static void PHP_SHA3_Init(PHP_SHA3_CTX* ctx,
151                           int bits) {
152 	memset(ctx, 0, sizeof(PHP_SHA3_CTX));
153 }
154 
PHP_SHA3_Update(PHP_SHA3_CTX * ctx,const unsigned char * buf,size_t count,size_t block_size)155 static void PHP_SHA3_Update(PHP_SHA3_CTX* ctx,
156                             const unsigned char* buf,
157                             size_t count,
158                             size_t block_size) {
159 	while (count > 0) {
160 		size_t len = block_size - ctx->pos;
161 
162 		if (len > count) {
163 			len = count;
164 		}
165 
166 		count -= len;
167 
168 		while (len-- > 0) {
169 			ctx->state[ctx->pos++] ^= *(buf++);
170 		}
171 
172 		if (ctx->pos >= block_size) {
173 			permute(ctx);
174 			ctx->pos = 0;
175 		}
176 	}
177 }
178 
PHP_SHA3_Final(unsigned char * digest,PHP_SHA3_CTX * ctx,size_t block_size,size_t digest_size)179 static void PHP_SHA3_Final(unsigned char* digest,
180                            PHP_SHA3_CTX* ctx,
181                            size_t block_size,
182                            size_t digest_size) {
183 	size_t len = digest_size;
184 
185 	// Pad state to finalize
186 	ctx->state[ctx->pos++] ^= 0x06;
187 	ctx->state[block_size-1] ^= 0x80;
188 	permute(ctx);
189 
190 	// Square output for digest
191 	for(;;) {
192 		int bs = (len < block_size) ? len : block_size;
193 		memcpy(digest, ctx->state, bs);
194 		digest += bs;
195 		len -= bs;
196 		if (!len) break;
197 		permute(ctx);
198 	}
199 
200 	// Zero out context
201 	ZEND_SECURE_ZERO(ctx, sizeof(PHP_SHA3_CTX));
202 }
203 
php_sha3_unserialize(php_hashcontext_object * hash,zend_long magic,const zval * zv,size_t block_size)204 static int php_sha3_unserialize(php_hashcontext_object *hash,
205 				zend_long magic,
206 				const zval *zv,
207 				size_t block_size)
208 {
209 	PHP_SHA3_CTX *ctx = (PHP_SHA3_CTX *) hash->context;
210 	int r = FAILURE;
211 	if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
212 		&& (r = php_hash_unserialize_spec(hash, zv, PHP_SHA3_SPEC)) == SUCCESS
213 		&& ctx->pos < block_size) {
214 		return SUCCESS;
215 	} else {
216 		return r != SUCCESS ? r : -2000;
217 	}
218 }
219 
220 // ==========================================================================
221 
222 #define DECLARE_SHA3_OPS(bits) \
223 void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args) { \
224 	PHP_SHA3_Init(ctx, bits); \
225 } \
226 void PHP_SHA3##bits##Update(PHP_SHA3_##bits##_CTX* ctx, \
227                             const unsigned char* input, \
228                             size_t inputLen) { \
229 	PHP_SHA3_Update(ctx, input, inputLen, \
230                     (1600 - (2 * bits)) >> 3); \
231 } \
232 void PHP_SHA3##bits##Final(unsigned char* digest, \
233                            PHP_SHA3_##bits##_CTX* ctx) { \
234 	PHP_SHA3_Final(digest, ctx, \
235                    (1600 - (2 * bits)) >> 3, \
236                    bits >> 3); \
237 } \
238 static int php_sha3##bits##_unserialize(php_hashcontext_object *hash, \
239 					zend_long magic, \
240 					const zval *zv) { \
241 	return php_sha3_unserialize(hash, magic, zv, (1600 - (2 * bits)) >> 3); \
242 } \
243 const php_hash_ops php_hash_sha3_##bits##_ops = { \
244 	"sha3-" #bits, \
245 	(php_hash_init_func_t) PHP_SHA3##bits##Init, \
246 	(php_hash_update_func_t) PHP_SHA3##bits##Update, \
247 	(php_hash_final_func_t) PHP_SHA3##bits##Final, \
248 	php_hash_copy, \
249 	php_hash_serialize, \
250 	php_sha3##bits##_unserialize, \
251 	PHP_SHA3_SPEC, \
252 	bits >> 3, \
253 	(1600 - (2 * bits)) >> 3, \
254 	sizeof(PHP_SHA3_##bits##_CTX), \
255 	1 \
256 }
257 
258 #else
259 
260 // ================= fast algo ==============================================
261 
262 #define SUCCESS SHA3_SUCCESS /* Avoid conflict between KeccacHash.h and zend_types.h */
263 #include "KeccakHash.h"
264 
265 /* KECCAK SERIALIZATION
266 
267    Keccak_HashInstance consists of:
268 	KeccakWidth1600_SpongeInstance {
269 		unsigned char state[200];
270 		unsigned int rate;         -- fixed for digest size
271 		unsigned int byteIOIndex;  -- in range [0, rate/8)
272 		int squeezing;             -- 0 normally, 1 only during finalize
273 	} sponge;
274 	unsigned int fixedOutputLength;    -- fixed for digest size
275 	unsigned char delimitedSuffix;     -- fixed for digest size
276 
277    NB If the external sha3/ library is updated, the serialization code
278    may need to be updated.
279 
280    The simpler SHA3 code's serialization states are not interchangeable with
281    Keccak. Furthermore, the Keccak sponge state is sensitive to architecture
282    -- 32-bit and 64-bit implementations produce different states. It does not
283    appear that the state is sensitive to endianness. */
284 
285 #if Keccak_HashInstance_ImplType == 64
286 /* corresponds to sha3/generic64lc */
287 # define PHP_HASH_SERIALIZE_MAGIC_KECCAK 100
288 #elif Keccak_HashInstance_ImplType == 32
289 /* corresponds to sha3/generic32lc */
290 # define PHP_HASH_SERIALIZE_MAGIC_KECCAK 101
291 #else
292 # error "Unknown Keccak_HashInstance_ImplType"
293 #endif
294 #define PHP_KECCAK_SPEC "b200IiIIB"
295 
php_keccak_serialize(const php_hashcontext_object * hash,zend_long * magic,zval * zv)296 static int php_keccak_serialize(const php_hashcontext_object *hash, zend_long *magic, zval *zv)
297 {
298 	*magic = PHP_HASH_SERIALIZE_MAGIC_KECCAK;
299 	return php_hash_serialize_spec(hash, zv, PHP_KECCAK_SPEC);
300 }
301 
php_keccak_unserialize(php_hashcontext_object * hash,zend_long magic,const zval * zv)302 static int php_keccak_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
303 {
304 	Keccak_HashInstance *ctx = (Keccak_HashInstance *) hash->context;
305 	int r = FAILURE;
306 	if (magic == PHP_HASH_SERIALIZE_MAGIC_KECCAK
307 		&& (r = php_hash_unserialize_spec(hash, zv, PHP_KECCAK_SPEC)) == SUCCESS
308 		&& ctx->sponge.byteIOIndex < ctx->sponge.rate / 8) {
309 		return SUCCESS;
310 	} else {
311 		return r != SUCCESS ? r : -2000;
312 	}
313 }
314 
315 // ==========================================================================
316 
317 #define DECLARE_SHA3_OPS(bits) \
318 void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args) { \
319 	ZEND_ASSERT(sizeof(Keccak_HashInstance) <= sizeof(PHP_SHA3_##bits##_CTX)); \
320 	Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx); \
321 } \
322 void PHP_SHA3##bits##Update(PHP_SHA3_##bits##_CTX* ctx, \
323                             const unsigned char* input, \
324                             size_t inputLen) { \
325 	Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
326 } \
327 void PHP_SHA3##bits##Final(unsigned char* digest, \
328                            PHP_SHA3_##bits##_CTX* ctx) { \
329 	Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
330 } \
331 const php_hash_ops php_hash_sha3_##bits##_ops = { \
332 	"sha3-" #bits, \
333 	(php_hash_init_func_t) PHP_SHA3##bits##Init, \
334 	(php_hash_update_func_t) PHP_SHA3##bits##Update, \
335 	(php_hash_final_func_t) PHP_SHA3##bits##Final, \
336 	php_hash_copy, \
337 	php_keccak_serialize, \
338 	php_keccak_unserialize, \
339 	PHP_KECCAK_SPEC, \
340 	bits >> 3, \
341 	(1600 - (2 * bits)) >> 3, \
342 	sizeof(PHP_SHA3_CTX), \
343 	1 \
344 }
345 
346 #endif
347 // ================= both algo ==============================================
348 
349 DECLARE_SHA3_OPS(224);
350 DECLARE_SHA3_OPS(256);
351 DECLARE_SHA3_OPS(384);
352 DECLARE_SHA3_OPS(512);
353 
354 #undef DECLARE_SHA3_OPS
355