Coverage Report

Created: 2025-06-13 06:43

/src/php-src/ext/hash/hash_sha3.c
Line
Count
Source (jump to first uncovered line)
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 int 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
  int r = FAILURE;
210
  if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
211
    && (r = php_hash_unserialize_spec(hash, zv, PHP_SHA3_SPEC)) == SUCCESS
212
    && ctx->pos < block_size) {
213
    return SUCCESS;
214
  } else {
215
    return r != SUCCESS ? r : -2000;
216
  }
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
392
#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
294
# 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
98
#define PHP_KECCAK_SPEC "b200IiIIB"
294
295
static zend_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 int php_keccak_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
302
147
{
303
147
  Keccak_HashInstance *ctx = (Keccak_HashInstance *) hash->context;
304
147
  int r = FAILURE;
305
147
  if (magic == PHP_HASH_SERIALIZE_MAGIC_KECCAK
306
147
    && (r = php_hash_unserialize_spec(hash, zv, PHP_KECCAK_SPEC)) == SUCCESS
307
147
    && ctx->sponge.byteIOIndex < ctx->sponge.rate / 8) {
308
93
    return SUCCESS;
309
93
  } else {
310
54
    return r != SUCCESS ? r : -2000;
311
54
  }
312
147
}
313
314
// ==========================================================================
315
316
#define DECLARE_SHA3_OPS(bits) \
317
147
void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args) { \
318
147
  ZEND_ASSERT(sizeof(Keccak_HashInstance) <= sizeof(PHP_SHA3_##bits##_CTX)); \
319
147
  Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx); \
320
147
} \
PHP_SHA3224Init
Line
Count
Source
317
65
void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args) { \
318
65
  ZEND_ASSERT(sizeof(Keccak_HashInstance) <= sizeof(PHP_SHA3_##bits##_CTX)); \
319
65
  Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx); \
320
65
} \
PHP_SHA3256Init
Line
Count
Source
317
29
void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args) { \
318
29
  ZEND_ASSERT(sizeof(Keccak_HashInstance) <= sizeof(PHP_SHA3_##bits##_CTX)); \
319
29
  Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx); \
320
29
} \
PHP_SHA3384Init
Line
Count
Source
317
21
void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args) { \
318
21
  ZEND_ASSERT(sizeof(Keccak_HashInstance) <= sizeof(PHP_SHA3_##bits##_CTX)); \
319
21
  Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx); \
320
21
} \
PHP_SHA3512Init
Line
Count
Source
317
32
void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args) { \
318
32
  ZEND_ASSERT(sizeof(Keccak_HashInstance) <= sizeof(PHP_SHA3_##bits##_CTX)); \
319
32
  Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx); \
320
32
} \
321
void PHP_SHA3##bits##Update(PHP_SHA3_##bits##_CTX* ctx, \
322
                            const unsigned char* input, \
323
93
                            size_t inputLen) { \
324
93
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
325
93
} \
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
25
                            size_t inputLen) { \
324
25
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
325
25
} \
PHP_SHA3384Update
Line
Count
Source
323
20
                            size_t inputLen) { \
324
20
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
325
20
} \
PHP_SHA3512Update
Line
Count
Source
323
31
                            size_t inputLen) { \
324
31
  Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \
325
31
} \
326
void PHP_SHA3##bits##Final(unsigned char* digest, \
327
93
                           PHP_SHA3_##bits##_CTX* ctx) { \
328
93
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
329
93
} \
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
25
                           PHP_SHA3_##bits##_CTX* ctx) { \
328
25
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
329
25
} \
PHP_SHA3384Final
Line
Count
Source
327
20
                           PHP_SHA3_##bits##_CTX* ctx) { \
328
20
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
329
20
} \
PHP_SHA3512Final
Line
Count
Source
327
31
                           PHP_SHA3_##bits##_CTX* ctx) { \
328
31
  Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \
329
31
} \
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
DECLARE_SHA3_OPS(224);
349
DECLARE_SHA3_OPS(256);
350
DECLARE_SHA3_OPS(384);
351
DECLARE_SHA3_OPS(512);
352
353
#undef DECLARE_SHA3_OPS