Coverage Report

Created: 2025-09-27 06:26

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 (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
34
static inline uint64_t rol64(uint64_t v, unsigned char b) {
35
  return (v << b) | (v >> (64 - b));
36
}
37
static inline unsigned char idx(unsigned char x, unsigned char y) {
38
  return x + (5 * y);
39
}
40
41
#ifdef WORDS_BIGENDIAN
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
}
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
}
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
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
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
150
static void PHP_SHA3_Init(PHP_SHA3_CTX* ctx,
151
                          int bits) {
152
  memset(ctx, 0, sizeof(PHP_SHA3_CTX));
153
}
154
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
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
203
static hash_spec_result 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
  hash_spec_result r = HASH_SPEC_FAILURE;
210
  if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
211
    && (r = php_hash_unserialize_spec(hash, zv, PHP_SHA3_SPEC)) == HASH_SPEC_SUCCESS
212
    && ctx->pos < block_size) {
213
    return HASH_SPEC_SUCCESS;
214
  }
215
216
    return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
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
288
# 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
91
#define PHP_KECCAK_SPEC "b200IiIIB"
294
295
static hash_spec_result php_keccak_serialize(const php_hashcontext_object *hash, zend_long *magic, zval *zv)
296
0
{
297
0
  *magic = PHP_HASH_SERIALIZE_MAGIC_KECCAK;
298
0
  return php_hash_serialize_spec(hash, zv, PHP_KECCAK_SPEC);
299
0
}
300
301
static hash_spec_result php_keccak_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
302
144
{
303
144
  Keccak_HashInstance *ctx = (Keccak_HashInstance *) hash->context;
304
144
  hash_spec_result r = HASH_SPEC_FAILURE;
305
144
  if (magic == PHP_HASH_SERIALIZE_MAGIC_KECCAK
306
91
    && (r = php_hash_unserialize_spec(hash, zv, PHP_KECCAK_SPEC)) == HASH_SPEC_SUCCESS
307
89
    && ctx->sponge.byteIOIndex < ctx->sponge.rate / 8) {
308
87
    return HASH_SPEC_SUCCESS;
309
87
  }
310
311
57
    return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
312
144
}
313
314
// ==========================================================================
315
316
#define DECLARE_SHA3_OPS(bits) \
317
144
void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args) { \
318
144
  ZEND_ASSERT(sizeof(Keccak_HashInstance) <= sizeof(PHP_SHA3_##bits##_CTX)); \
319
144
  Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx); \
320
144
} \
321
void PHP_SHA3##bits##Update(PHP_SHA3_##bits##_CTX* ctx, \
322
                            const unsigned char* input, \
323
87
                            size_t inputLen) { \
324
87
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
325
87
} \
PHP_SHA3224Update
Line
Count
Source
323
17
                            size_t inputLen) { \
324
17
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
325
17
} \
PHP_SHA3256Update
Line
Count
Source
323
17
                            size_t inputLen) { \
324
17
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
325
17
} \
PHP_SHA3384Update
Line
Count
Source
323
25
                            size_t inputLen) { \
324
25
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
325
25
} \
PHP_SHA3512Update
Line
Count
Source
323
28
                            size_t inputLen) { \
324
28
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
325
28
} \
326
void PHP_SHA3##bits##Final(unsigned char* digest, \
327
87
                           PHP_SHA3_##bits##_CTX* ctx) { \
328
87
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
329
87
} \
PHP_SHA3224Final
Line
Count
Source
327
17
                           PHP_SHA3_##bits##_CTX* ctx) { \
328
17
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
329
17
} \
PHP_SHA3256Final
Line
Count
Source
327
17
                           PHP_SHA3_##bits##_CTX* ctx) { \
328
17
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
329
17
} \
PHP_SHA3384Final
Line
Count
Source
327
25
                           PHP_SHA3_##bits##_CTX* ctx) { \
328
25
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
329
25
} \
PHP_SHA3512Final
Line
Count
Source
327
28
                           PHP_SHA3_##bits##_CTX* ctx) { \
328
28
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
329
28
} \
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
25
DECLARE_SHA3_OPS(224);
349
27
DECLARE_SHA3_OPS(256);
350
62
DECLARE_SHA3_OPS(384);
351
DECLARE_SHA3_OPS(512);
352
353
#undef DECLARE_SHA3_OPS