Coverage Report

Created: 2026-09-14 06:25

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