Coverage Report

Created: 2026-07-25 06:39

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
272
# 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
93
#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
136
{
302
136
  Keccak_HashInstance *ctx = (Keccak_HashInstance *) hash->context;
303
136
  hash_spec_result r = HASH_SPEC_FAILURE;
304
136
  if (magic == PHP_HASH_SERIALIZE_MAGIC_KECCAK
305
93
    && (r = php_hash_unserialize_spec(hash, zv, PHP_KECCAK_SPEC)) == HASH_SPEC_SUCCESS
306
91
    && ctx->sponge.byteIOIndex < ctx->sponge.rate / 8) {
307
87
    return HASH_SPEC_SUCCESS;
308
87
  }
309
310
49
    return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
311
136
}
312
313
// ==========================================================================
314
315
#define DECLARE_SHA3_OPS(bits) \
316
136
void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args) { \
317
136
  ZEND_ASSERT(sizeof(Keccak_HashInstance) <= sizeof(PHP_SHA3_##bits##_CTX)); \
318
136
  Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx); \
319
136
} \
320
void PHP_SHA3##bits##Update(PHP_SHA3_##bits##_CTX* ctx, \
321
                            const unsigned char* input, \
322
87
                            size_t inputLen) { \
323
87
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
324
87
} \
PHP_SHA3224Update
Line
Count
Source
322
16
                            size_t inputLen) { \
323
16
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
324
16
} \
PHP_SHA3256Update
Line
Count
Source
322
18
                            size_t inputLen) { \
323
18
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
324
18
} \
PHP_SHA3384Update
Line
Count
Source
322
15
                            size_t inputLen) { \
323
15
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
324
15
} \
PHP_SHA3512Update
Line
Count
Source
322
38
                            size_t inputLen) { \
323
38
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
324
38
} \
325
void PHP_SHA3##bits##Final(unsigned char* digest, \
326
87
                           PHP_SHA3_##bits##_CTX* ctx) { \
327
87
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
328
87
} \
PHP_SHA3224Final
Line
Count
Source
326
16
                           PHP_SHA3_##bits##_CTX* ctx) { \
327
16
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
328
16
} \
PHP_SHA3256Final
Line
Count
Source
326
18
                           PHP_SHA3_##bits##_CTX* ctx) { \
327
18
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
328
18
} \
PHP_SHA3384Final
Line
Count
Source
326
15
                           PHP_SHA3_##bits##_CTX* ctx) { \
327
15
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
328
15
} \
PHP_SHA3512Final
Line
Count
Source
326
38
                           PHP_SHA3_##bits##_CTX* ctx) { \
327
38
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
328
38
} \
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
16
DECLARE_SHA3_OPS(224);
349
21
DECLARE_SHA3_OPS(256);
350
55
DECLARE_SHA3_OPS(384);
351
DECLARE_SHA3_OPS(512);
352
353
#undef DECLARE_SHA3_OPS