Coverage Report

Created: 2026-07-25 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/standard/md5.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: Alexander Peslyak (Solar Designer) <solar at openwall.com>   |
12
   |         Lachlan Roche                                                |
13
   |         Alessandro Astarita <aleast@capri.it>                        |
14
   +----------------------------------------------------------------------+
15
*/
16
17
#include "php.h"
18
#include "md5.h"
19
20
PHPAPI void make_digest(char *md5str, const unsigned char *digest)
21
0
{
22
0
  make_digest_ex(md5str, digest, 16);
23
0
}
24
25
PHPAPI void make_digest_ex(char *md5str, const unsigned char *digest, size_t len)
26
376
{
27
376
  static const char hexits[17] = "0123456789abcdef";
28
29
6.39k
  for (size_t i = 0; i < len; i++) {
30
6.01k
    md5str[i * 2]       = hexits[digest[i] >> 4];
31
6.01k
    md5str[(i * 2) + 1] = hexits[digest[i] &  0x0F];
32
6.01k
  }
33
376
  md5str[len * 2] = '\0';
34
376
}
35
36
/* Calculate the md5 hash of a string */
37
PHP_FUNCTION(md5)
38
391
{
39
391
  zend_string *arg;
40
391
  PHP_MD5_CTX context;
41
391
  unsigned char digest[16];
42
391
  bool raw_output = false;
43
44
1.17k
  ZEND_PARSE_PARAMETERS_START(1, 2)
45
1.56k
    Z_PARAM_STR(arg)
46
388
    Z_PARAM_OPTIONAL
47
800
    Z_PARAM_BOOL(raw_output)
48
391
  ZEND_PARSE_PARAMETERS_END();
49
50
388
  PHP_MD5Init(&context);
51
388
  PHP_MD5Update(&context, ZSTR_VAL(arg), ZSTR_LEN(arg));
52
388
  PHP_MD5Final(digest, &context);
53
388
  if (raw_output) {
54
12
    RETURN_STRINGL((char *) digest, 16);
55
376
  } else {
56
376
    RETVAL_NEW_STR(zend_string_alloc(32, 0));
57
376
    make_digest_ex(Z_STRVAL_P(return_value), digest, 16);
58
376
  }
59
60
388
}
61
62
/* Calculate the md5 hash of given filename */
63
PHP_FUNCTION(md5_file)
64
0
{
65
0
  char          *arg;
66
0
  size_t           arg_len;
67
0
  bool raw_output = false;
68
0
  unsigned char buf[1024];
69
0
  unsigned char digest[16];
70
0
  PHP_MD5_CTX   context;
71
0
  ssize_t       n;
72
0
  php_stream    *stream;
73
74
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
75
0
    Z_PARAM_PATH(arg, arg_len)
76
0
    Z_PARAM_OPTIONAL
77
0
    Z_PARAM_BOOL(raw_output)
78
0
  ZEND_PARSE_PARAMETERS_END();
79
80
0
  stream = php_stream_open_wrapper(arg, "rb", REPORT_ERRORS, NULL);
81
0
  if (!stream) {
82
0
    RETURN_FALSE;
83
0
  }
84
85
0
  PHP_MD5Init(&context);
86
87
0
  while ((n = php_stream_read(stream, (char*)buf, sizeof(buf))) > 0) {
88
0
    PHP_MD5Update(&context, buf, n);
89
0
  }
90
91
  /* XXX this probably can be improved with some number of retries */
92
0
  if (!php_stream_eof(stream)) {
93
0
    php_stream_close(stream);
94
0
    PHP_MD5Final(digest, &context);
95
96
0
    RETURN_FALSE;
97
0
  }
98
99
0
  php_stream_close(stream);
100
101
0
  PHP_MD5Final(digest, &context);
102
103
0
  if (raw_output) {
104
0
    RETURN_STRINGL((char *) digest, 16);
105
0
  } else {
106
0
    RETVAL_NEW_STR(zend_string_alloc(32, 0));
107
0
    make_digest_ex(Z_STRVAL_P(return_value), digest, 16);
108
0
  }
109
0
}
110
111
/*
112
 * This is an OpenSSL-compatible implementation of the RSA Data Security,
113
 * Inc. MD5 Message-Digest Algorithm (RFC 1321).
114
 *
115
 * Written by Solar Designer <solar at openwall.com> in 2001, and placed
116
 * in the public domain.  There's absolutely no warranty.
117
 *
118
 * This differs from Colin Plumb's older public domain implementation in
119
 * that no 32-bit integer data type is required, there's no compile-time
120
 * endianness configuration, and the function prototypes match OpenSSL's.
121
 * The primary goals are portability and ease of use.
122
 *
123
 * This implementation is meant to be fast, but not as fast as possible.
124
 * Some known optimizations are not included to reduce source code size
125
 * and avoid compile-time configuration.
126
 */
127
128
#include <string.h>
129
130
/*
131
 * The basic MD5 functions.
132
 *
133
 * F and G are optimized compared to their RFC 1321 definitions for
134
 * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
135
 * implementation.
136
 */
137
692k
#define F(x, y, z)      ((z) ^ ((x) & ((y) ^ (z))))
138
692k
#define G(x, y, z)      ((y) ^ ((z) & ((x) ^ (y))))
139
692k
#define H(x, y, z)      ((x) ^ (y) ^ (z))
140
692k
#define I(x, y, z)      ((y) ^ ((x) | ~(z)))
141
142
/*
143
 * The MD5 transformation for all four rounds.
144
 */
145
#define STEP(f, a, b, c, d, x, t, s) \
146
2.76M
  (a) += f((b), (c), (d)) + (x) + (t); \
147
2.76M
  (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
148
2.76M
  (a) += (b);
149
150
/*
151
 * SET reads 4 input bytes in little-endian byte order and stores them
152
 * in a properly aligned word in host byte order.
153
 *
154
 * The check for little-endian architectures that tolerate unaligned
155
 * memory accesses is just an optimization.  Nothing will break if it
156
 * doesn't work.
157
 */
158
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
159
typedef ZEND_SET_ALIGNED(1, uint32_t unaligned_uint32_t);
160
# define SET(n) \
161
  (*(unaligned_uint32_t *)&ptr[(n) * 4])
162
# define GET(n) \
163
  SET(n)
164
#else
165
# define SET(n) \
166
  (ctx->block[(n)] = \
167
  (uint32_t)ptr[(n) * 4] | \
168
  ((uint32_t)ptr[(n) * 4 + 1] << 8) | \
169
  ((uint32_t)ptr[(n) * 4 + 2] << 16) | \
170
  ((uint32_t)ptr[(n) * 4 + 3] << 24))
171
# define GET(n) \
172
  (ctx->block[(n)])
173
#endif
174
175
/*
176
 * This processes one or more 64-byte data blocks, but does NOT update
177
 * the bit counters.  There are no alignment requirements.
178
 */
179
static const void *body(PHP_MD5_CTX *ctx, const void *data, size_t size)
180
599
{
181
599
  const unsigned char *ptr;
182
599
  uint32_t a, b, c, d;
183
599
  uint32_t saved_a, saved_b, saved_c, saved_d;
184
185
599
  ptr = data;
186
187
599
  a = ctx->a;
188
599
  b = ctx->b;
189
599
  c = ctx->c;
190
599
  d = ctx->d;
191
192
43.2k
  do {
193
43.2k
    saved_a = a;
194
43.2k
    saved_b = b;
195
43.2k
    saved_c = c;
196
43.2k
    saved_d = d;
197
198
/* Round 1 */
199
43.2k
    STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
200
43.2k
    STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
201
43.2k
    STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
202
43.2k
    STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
203
43.2k
    STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
204
43.2k
    STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
205
43.2k
    STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
206
43.2k
    STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
207
43.2k
    STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
208
43.2k
    STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
209
43.2k
    STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
210
43.2k
    STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
211
43.2k
    STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
212
43.2k
    STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
213
43.2k
    STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
214
43.2k
    STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
215
216
/* Round 2 */
217
43.2k
    STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
218
43.2k
    STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
219
43.2k
    STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
220
43.2k
    STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
221
43.2k
    STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
222
43.2k
    STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
223
43.2k
    STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
224
43.2k
    STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
225
43.2k
    STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
226
43.2k
    STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
227
43.2k
    STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
228
43.2k
    STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
229
43.2k
    STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
230
43.2k
    STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
231
43.2k
    STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
232
43.2k
    STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
233
234
/* Round 3 */
235
43.2k
    STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
236
43.2k
    STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
237
43.2k
    STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
238
43.2k
    STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
239
43.2k
    STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
240
43.2k
    STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
241
43.2k
    STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
242
43.2k
    STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
243
43.2k
    STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
244
43.2k
    STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
245
43.2k
    STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
246
43.2k
    STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
247
43.2k
    STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
248
43.2k
    STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
249
43.2k
    STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
250
43.2k
    STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
251
252
/* Round 4 */
253
43.2k
    STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
254
43.2k
    STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
255
43.2k
    STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
256
43.2k
    STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
257
43.2k
    STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
258
43.2k
    STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
259
43.2k
    STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
260
43.2k
    STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
261
43.2k
    STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
262
43.2k
    STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
263
43.2k
    STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
264
43.2k
    STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
265
43.2k
    STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
266
43.2k
    STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
267
43.2k
    STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
268
43.2k
    STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
269
270
43.2k
    a += saved_a;
271
43.2k
    b += saved_b;
272
43.2k
    c += saved_c;
273
43.2k
    d += saved_d;
274
275
43.2k
    ptr += 64;
276
43.2k
  } while (size -= 64);
277
278
599
  ctx->a = a;
279
599
  ctx->b = b;
280
599
  ctx->c = c;
281
599
  ctx->d = d;
282
283
599
  return ptr;
284
599
}
285
286
PHPAPI void PHP_MD5InitArgs(PHP_MD5_CTX *ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args)
287
461
{
288
461
  ctx->a = 0x67452301;
289
461
  ctx->b = 0xefcdab89;
290
461
  ctx->c = 0x98badcfe;
291
461
  ctx->d = 0x10325476;
292
293
461
  ctx->lo = 0;
294
461
  ctx->hi = 0;
295
461
}
296
297
PHPAPI void PHP_MD5Update(PHP_MD5_CTX *ctx, const void *data, size_t size)
298
586
{
299
586
  uint32_t saved_lo;
300
586
  uint32_t used, free;
301
302
586
  saved_lo = ctx->lo;
303
586
  if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) {
304
50
    ctx->hi++;
305
50
  }
306
586
  ctx->hi += size >> 29;
307
308
586
  used = saved_lo & 0x3f;
309
310
586
  if (used) {
311
162
    free = 64 - used;
312
313
162
    if (size < free) {
314
98
      memcpy(&ctx->buffer[used], data, size);
315
98
      return;
316
98
    }
317
318
64
    memcpy(&ctx->buffer[used], data, free);
319
64
    data = (unsigned char *)data + free;
320
64
    size -= free;
321
64
    body(ctx, ctx->buffer, 64);
322
64
  }
323
324
488
  if (size >= 64) {
325
63
    data = body(ctx, data, size & ~(size_t)0x3f);
326
63
    size &= 0x3f;
327
63
  }
328
329
488
  memcpy(ctx->buffer, data, size);
330
488
}
331
332
PHPAPI void PHP_MD5Final(unsigned char *result, PHP_MD5_CTX *ctx)
333
458
{
334
458
  uint32_t used, free;
335
336
458
  used = ctx->lo & 0x3f;
337
338
458
  ctx->buffer[used++] = 0x80;
339
340
458
  free = 64 - used;
341
342
458
  if (free < 8) {
343
14
    memset(&ctx->buffer[used], 0, free);
344
14
    body(ctx, ctx->buffer, 64);
345
14
    used = 0;
346
14
    free = 64;
347
14
  }
348
349
458
  memset(&ctx->buffer[used], 0, free - 8);
350
351
458
  ctx->lo <<= 3;
352
458
  ctx->buffer[56] = ctx->lo;
353
458
  ctx->buffer[57] = ctx->lo >> 8;
354
458
  ctx->buffer[58] = ctx->lo >> 16;
355
458
  ctx->buffer[59] = ctx->lo >> 24;
356
458
  ctx->buffer[60] = ctx->hi;
357
458
  ctx->buffer[61] = ctx->hi >> 8;
358
458
  ctx->buffer[62] = ctx->hi >> 16;
359
458
  ctx->buffer[63] = ctx->hi >> 24;
360
361
458
  body(ctx, ctx->buffer, 64);
362
363
458
  result[0] = ctx->a;
364
458
  result[1] = ctx->a >> 8;
365
458
  result[2] = ctx->a >> 16;
366
458
  result[3] = ctx->a >> 24;
367
458
  result[4] = ctx->b;
368
458
  result[5] = ctx->b >> 8;
369
458
  result[6] = ctx->b >> 16;
370
458
  result[7] = ctx->b >> 24;
371
458
  result[8] = ctx->c;
372
458
  result[9] = ctx->c >> 8;
373
458
  result[10] = ctx->c >> 16;
374
458
  result[11] = ctx->c >> 24;
375
458
  result[12] = ctx->d;
376
458
  result[13] = ctx->d >> 8;
377
458
  result[14] = ctx->d >> 16;
378
458
  result[15] = ctx->d >> 24;
379
380
458
  ZEND_SECURE_ZERO(ctx, sizeof(*ctx));
381
458
}