Coverage Report

Created: 2026-10-28 06:20

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