Coverage Report

Created: 2026-06-02 06:36

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
}
254
255
#else
256
257
// ================= fast algo ==============================================
258
259
#define SUCCESS SHA3_SUCCESS /* Avoid conflict between KeccacHash.h and zend_types.h */
260
#include "KeccakHash.h"
261
262
/* KECCAK SERIALIZATION
263
264
   Keccak_HashInstance consists of:
265
  KeccakWidth1600_SpongeInstance {
266
    unsigned char state[200];
267
    unsigned int rate;         -- fixed for digest size
268
    unsigned int byteIOIndex;  -- in range [0, rate/8)
269
    int squeezing;             -- 0 normally, 1 only during finalize
270
  } sponge;
271
  unsigned int fixedOutputLength;    -- fixed for digest size
272
  unsigned char delimitedSuffix;     -- fixed for digest size
273
274
   NB If the external sha3/ library is updated, the serialization code
275
   may need to be updated.
276
277
   The simpler SHA3 code's serialization states are not interchangeable with
278
   Keccak. Furthermore, the Keccak sponge state is sensitive to architecture
279
   -- 32-bit and 64-bit implementations produce different states. It does not
280
   appear that the state is sensitive to endianness. */
281
282
#if Keccak_HashInstance_ImplType == 64
283
/* corresponds to sha3/generic64lc */
284
0
# define PHP_HASH_SERIALIZE_MAGIC_KECCAK 100
285
#elif Keccak_HashInstance_ImplType == 32
286
/* corresponds to sha3/generic32lc */
287
# define PHP_HASH_SERIALIZE_MAGIC_KECCAK 101
288
#else
289
# error "Unknown Keccak_HashInstance_ImplType"
290
#endif
291
0
#define PHP_KECCAK_SPEC "b200IiIIB"
292
293
static hash_spec_result php_keccak_serialize(const php_hashcontext_object *hash, zend_long *magic, zval *zv)
294
0
{
295
0
  *magic = PHP_HASH_SERIALIZE_MAGIC_KECCAK;
296
0
  return php_hash_serialize_spec(hash, zv, PHP_KECCAK_SPEC);
297
0
}
298
299
static hash_spec_result php_keccak_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
300
0
{
301
0
  Keccak_HashInstance *ctx = (Keccak_HashInstance *) hash->context;
302
0
  hash_spec_result r = HASH_SPEC_FAILURE;
303
0
  if (magic == PHP_HASH_SERIALIZE_MAGIC_KECCAK
304
0
    && (r = php_hash_unserialize_spec(hash, zv, PHP_KECCAK_SPEC)) == HASH_SPEC_SUCCESS
305
0
    && ctx->sponge.byteIOIndex < ctx->sponge.rate / 8) {
306
0
    return HASH_SPEC_SUCCESS;
307
0
  }
308
309
0
    return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
310
0
}
311
312
// ==========================================================================
313
314
#define DECLARE_SHA3_OPS(bits) \
315
0
void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args) { \
316
0
  ZEND_ASSERT(sizeof(Keccak_HashInstance) <= sizeof(PHP_SHA3_##bits##_CTX)); \
317
0
  Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx); \
318
0
} \
319
void PHP_SHA3##bits##Update(PHP_SHA3_##bits##_CTX* ctx, \
320
                            const unsigned char* input, \
321
0
                            size_t inputLen) { \
322
0
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
323
0
} \
Unexecuted instantiation: PHP_SHA3224Update
Unexecuted instantiation: PHP_SHA3256Update
Unexecuted instantiation: PHP_SHA3384Update
Unexecuted instantiation: PHP_SHA3512Update
324
void PHP_SHA3##bits##Final(unsigned char* digest, \
325
0
                           PHP_SHA3_##bits##_CTX* ctx) { \
326
0
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
327
0
} \
Unexecuted instantiation: PHP_SHA3224Final
Unexecuted instantiation: PHP_SHA3256Final
Unexecuted instantiation: PHP_SHA3384Final
Unexecuted instantiation: PHP_SHA3512Final
328
const php_hash_ops php_hash_sha3_##bits##_ops = { \
329
  "sha3-" #bits, \
330
  (php_hash_init_func_t) PHP_SHA3##bits##Init, \
331
  (php_hash_update_func_t) PHP_SHA3##bits##Update, \
332
  (php_hash_final_func_t) PHP_SHA3##bits##Final, \
333
  php_hash_copy, \
334
  php_keccak_serialize, \
335
  php_keccak_unserialize, \
336
  PHP_KECCAK_SPEC, \
337
  bits >> 3, \
338
  (1600 - (2 * bits)) >> 3, \
339
  sizeof(PHP_SHA3_CTX), \
340
  1 \
341
}
342
343
#endif
344
// ================= both algo ==============================================
345
346
0
DECLARE_SHA3_OPS(224);
347
0
DECLARE_SHA3_OPS(256);
348
0
DECLARE_SHA3_OPS(384);
349
DECLARE_SHA3_OPS(512);
350
351
#undef DECLARE_SHA3_OPS