xref: /PHP-8.4/ext/hash/hash_sha3.c (revision 937c4e4a)
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 		digest = zend_mempcpy(digest, ctx->state, bs);
194 		len -= bs;
195 		if (!len) break;
196 		permute(ctx);
197 	}
198 
199 	// Zero out context
200 	ZEND_SECURE_ZERO(ctx, sizeof(PHP_SHA3_CTX));
201 }
202 
php_sha3_unserialize(php_hashcontext_object * hash,zend_long magic,const zval * zv,size_t block_size)203 static int php_sha3_unserialize(php_hashcontext_object *hash,
204 				zend_long magic,
205 				const zval *zv,
206 				size_t block_size)
207 {
208 	PHP_SHA3_CTX *ctx = (PHP_SHA3_CTX *) hash->context;
209 	int r = FAILURE;
210 	if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
211 		&& (r = php_hash_unserialize_spec(hash, zv, PHP_SHA3_SPEC)) == SUCCESS
212 		&& ctx->pos < block_size) {
213 		return SUCCESS;
214 	} else {
215 		return r != SUCCESS ? r : -2000;
216 	}
217 }
218 
219 // ==========================================================================
220 
221 #define DECLARE_SHA3_OPS(bits) \
222 void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args) { \
223 	PHP_SHA3_Init(ctx, bits); \
224 } \
225 void PHP_SHA3##bits##Update(PHP_SHA3_##bits##_CTX* ctx, \
226                             const unsigned char* input, \
227                             size_t inputLen) { \
228 	PHP_SHA3_Update(ctx, input, inputLen, \
229                     (1600 - (2 * bits)) >> 3); \
230 } \
231 void PHP_SHA3##bits##Final(unsigned char* digest, \
232                            PHP_SHA3_##bits##_CTX* ctx) { \
233 	PHP_SHA3_Final(digest, ctx, \
234                    (1600 - (2 * bits)) >> 3, \
235                    bits >> 3); \
236 } \
237 static int php_sha3##bits##_unserialize(php_hashcontext_object *hash, \
238 					zend_long magic, \
239 					const zval *zv) { \
240 	return php_sha3_unserialize(hash, magic, zv, (1600 - (2 * bits)) >> 3); \
241 } \
242 const php_hash_ops php_hash_sha3_##bits##_ops = { \
243 	"sha3-" #bits, \
244 	(php_hash_init_func_t) PHP_SHA3##bits##Init, \
245 	(php_hash_update_func_t) PHP_SHA3##bits##Update, \
246 	(php_hash_final_func_t) PHP_SHA3##bits##Final, \
247 	php_hash_copy, \
248 	php_hash_serialize, \
249 	php_sha3##bits##_unserialize, \
250 	PHP_SHA3_SPEC, \
251 	bits >> 3, \
252 	(1600 - (2 * bits)) >> 3, \
253 	sizeof(PHP_SHA3_##bits##_CTX), \
254 	1 \
255 }
256 
257 #else
258 
259 // ================= fast algo ==============================================
260 
261 #define SUCCESS SHA3_SUCCESS /* Avoid conflict between KeccacHash.h and zend_types.h */
262 #include "KeccakHash.h"
263 
264 /* KECCAK SERIALIZATION
265 
266    Keccak_HashInstance consists of:
267 	KeccakWidth1600_SpongeInstance {
268 		unsigned char state[200];
269 		unsigned int rate;         -- fixed for digest size
270 		unsigned int byteIOIndex;  -- in range [0, rate/8)
271 		int squeezing;             -- 0 normally, 1 only during finalize
272 	} sponge;
273 	unsigned int fixedOutputLength;    -- fixed for digest size
274 	unsigned char delimitedSuffix;     -- fixed for digest size
275 
276    NB If the external sha3/ library is updated, the serialization code
277    may need to be updated.
278 
279    The simpler SHA3 code's serialization states are not interchangeable with
280    Keccak. Furthermore, the Keccak sponge state is sensitive to architecture
281    -- 32-bit and 64-bit implementations produce different states. It does not
282    appear that the state is sensitive to endianness. */
283 
284 #if Keccak_HashInstance_ImplType == 64
285 /* corresponds to sha3/generic64lc */
286 # define PHP_HASH_SERIALIZE_MAGIC_KECCAK 100
287 #elif Keccak_HashInstance_ImplType == 32
288 /* corresponds to sha3/generic32lc */
289 # define PHP_HASH_SERIALIZE_MAGIC_KECCAK 101
290 #else
291 # error "Unknown Keccak_HashInstance_ImplType"
292 #endif
293 #define PHP_KECCAK_SPEC "b200IiIIB"
294 
php_keccak_serialize(const php_hashcontext_object * hash,zend_long * magic,zval * zv)295 static zend_result php_keccak_serialize(const php_hashcontext_object *hash, zend_long *magic, zval *zv)
296 {
297 	*magic = PHP_HASH_SERIALIZE_MAGIC_KECCAK;
298 	return php_hash_serialize_spec(hash, zv, PHP_KECCAK_SPEC);
299 }
300 
php_keccak_unserialize(php_hashcontext_object * hash,zend_long magic,const zval * zv)301 static int php_keccak_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
302 {
303 	Keccak_HashInstance *ctx = (Keccak_HashInstance *) hash->context;
304 	int r = FAILURE;
305 	if (magic == PHP_HASH_SERIALIZE_MAGIC_KECCAK
306 		&& (r = php_hash_unserialize_spec(hash, zv, PHP_KECCAK_SPEC)) == SUCCESS
307 		&& ctx->sponge.byteIOIndex < ctx->sponge.rate / 8) {
308 		return SUCCESS;
309 	} else {
310 		return r != SUCCESS ? r : -2000;
311 	}
312 }
313 
314 // ==========================================================================
315 
316 #define DECLARE_SHA3_OPS(bits) \
317 void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args) { \
318 	ZEND_ASSERT(sizeof(Keccak_HashInstance) <= sizeof(PHP_SHA3_##bits##_CTX)); \
319 	Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx); \
320 } \
321 void PHP_SHA3##bits##Update(PHP_SHA3_##bits##_CTX* ctx, \
322                             const unsigned char* input, \
323                             size_t inputLen) { \
324 	Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
325 } \
326 void PHP_SHA3##bits##Final(unsigned char* digest, \
327                            PHP_SHA3_##bits##_CTX* ctx) { \
328 	Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
329 } \
330 const php_hash_ops php_hash_sha3_##bits##_ops = { \
331 	"sha3-" #bits, \
332 	(php_hash_init_func_t) PHP_SHA3##bits##Init, \
333 	(php_hash_update_func_t) PHP_SHA3##bits##Update, \
334 	(php_hash_final_func_t) PHP_SHA3##bits##Final, \
335 	php_hash_copy, \
336 	php_keccak_serialize, \
337 	php_keccak_unserialize, \
338 	PHP_KECCAK_SPEC, \
339 	bits >> 3, \
340 	(1600 - (2 * bits)) >> 3, \
341 	sizeof(PHP_SHA3_CTX), \
342 	1 \
343 }
344 
345 #endif
346 // ================= both algo ==============================================
347 
348 DECLARE_SHA3_OPS(224);
349 DECLARE_SHA3_OPS(256);
350 DECLARE_SHA3_OPS(384);
351 DECLARE_SHA3_OPS(512);
352 
353 #undef DECLARE_SHA3_OPS
354