Coverage Report

Created: 2026-06-13 07:01

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
500
{
27
500
  static const char hexits[17] = "0123456789abcdef";
28
29
8.50k
  for (size_t i = 0; i < len; i++) {
30
8.00k
    md5str[i * 2]       = hexits[digest[i] >> 4];
31
8.00k
    md5str[(i * 2) + 1] = hexits[digest[i] &  0x0F];
32
8.00k
  }
33
500
  md5str[len * 2] = '\0';
34
500
}
35
36
/* Calculate the md5 hash of a string */
37
PHP_FUNCTION(md5)
38
509
{
39
509
  zend_string *arg;
40
509
  PHP_MD5_CTX context;
41
509
  unsigned char digest[16];
42
509
  bool raw_output = false;
43
44
1.52k
  ZEND_PARSE_PARAMETERS_START(1, 2)
45
2.03k
    Z_PARAM_STR(arg)
46
506
    Z_PARAM_OPTIONAL
47
1.02k
    Z_PARAM_BOOL(raw_output)
48
509
  ZEND_PARSE_PARAMETERS_END();
49
50
506
  PHP_MD5Init(&context);
51
506
  PHP_MD5Update(&context, ZSTR_VAL(arg), ZSTR_LEN(arg));
52
506
  PHP_MD5Final(digest, &context);
53
506
  if (raw_output) {
54
6
    RETURN_STRINGL((char *) digest, 16);
55
500
  } else {
56
500
    RETVAL_NEW_STR(zend_string_alloc(32, 0));
57
500
    make_digest_ex(Z_STRVAL_P(return_value), digest, 16);
58
500
  }
59
60
506
}
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
648k
#define F(x, y, z)      ((z) ^ ((x) & ((y) ^ (z))))
138
648k
#define G(x, y, z)      ((y) ^ ((z) & ((x) ^ (y))))
139
648k
#define H(x, y, z)      ((x) ^ (y) ^ (z))
140
648k
#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.59M
  (a) += f((b), (c), (d)) + (x) + (t); \
147
2.59M
  (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
148
2.59M
  (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
706
{
181
706
  const unsigned char *ptr;
182
706
  uint32_t a, b, c, d;
183
706
  uint32_t saved_a, saved_b, saved_c, saved_d;
184
185
706
  ptr = data;
186
187
706
  a = ctx->a;
188
706
  b = ctx->b;
189
706
  c = ctx->c;
190
706
  d = ctx->d;
191
192
40.5k
  do {
193
40.5k
    saved_a = a;
194
40.5k
    saved_b = b;
195
40.5k
    saved_c = c;
196
40.5k
    saved_d = d;
197
198
/* Round 1 */
199
40.5k
    STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
200
40.5k
    STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
201
40.5k
    STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
202
40.5k
    STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
203
40.5k
    STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
204
40.5k
    STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
205
40.5k
    STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
206
40.5k
    STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
207
40.5k
    STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
208
40.5k
    STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
209
40.5k
    STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
210
40.5k
    STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
211
40.5k
    STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
212
40.5k
    STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
213
40.5k
    STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
214
40.5k
    STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
215
216
/* Round 2 */
217
40.5k
    STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
218
40.5k
    STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
219
40.5k
    STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
220
40.5k
    STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
221
40.5k
    STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
222
40.5k
    STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
223
40.5k
    STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
224
40.5k
    STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
225
40.5k
    STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
226
40.5k
    STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
227
40.5k
    STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
228
40.5k
    STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
229
40.5k
    STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
230
40.5k
    STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
231
40.5k
    STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
232
40.5k
    STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
233
234
/* Round 3 */
235
40.5k
    STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
236
40.5k
    STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
237
40.5k
    STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
238
40.5k
    STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
239
40.5k
    STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
240
40.5k
    STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
241
40.5k
    STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
242
40.5k
    STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
243
40.5k
    STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
244
40.5k
    STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
245
40.5k
    STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
246
40.5k
    STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
247
40.5k
    STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
248
40.5k
    STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
249
40.5k
    STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
250
40.5k
    STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
251
252
/* Round 4 */
253
40.5k
    STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
254
40.5k
    STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
255
40.5k
    STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
256
40.5k
    STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
257
40.5k
    STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
258
40.5k
    STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
259
40.5k
    STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
260
40.5k
    STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
261
40.5k
    STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
262
40.5k
    STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
263
40.5k
    STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
264
40.5k
    STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
265
40.5k
    STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
266
40.5k
    STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
267
40.5k
    STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
268
40.5k
    STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
269
270
40.5k
    a += saved_a;
271
40.5k
    b += saved_b;
272
40.5k
    c += saved_c;
273
40.5k
    d += saved_d;
274
275
40.5k
    ptr += 64;
276
40.5k
  } while (size -= 64);
277
278
706
  ctx->a = a;
279
706
  ctx->b = b;
280
706
  ctx->c = c;
281
706
  ctx->d = d;
282
283
706
  return ptr;
284
706
}
285
286
PHPAPI void PHP_MD5InitArgs(PHP_MD5_CTX *ctx, ZEND_ATTRIBUTE_UNUSED HashTable *args)
287
576
{
288
576
  ctx->a = 0x67452301;
289
576
  ctx->b = 0xefcdab89;
290
576
  ctx->c = 0x98badcfe;
291
576
  ctx->d = 0x10325476;
292
293
576
  ctx->lo = 0;
294
576
  ctx->hi = 0;
295
576
}
296
297
PHPAPI void PHP_MD5Update(PHP_MD5_CTX *ctx, const void *data, size_t size)
298
703
{
299
703
  uint32_t saved_lo;
300
703
  uint32_t used, free;
301
302
703
  saved_lo = ctx->lo;
303
703
  if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) {
304
51
    ctx->hi++;
305
51
  }
306
703
  ctx->hi += size >> 29;
307
308
703
  used = saved_lo & 0x3f;
309
310
703
  if (used) {
311
164
    free = 64 - used;
312
313
164
    if (size < free) {
314
99
      memcpy(&ctx->buffer[used], data, size);
315
99
      return;
316
99
    }
317
318
65
    memcpy(&ctx->buffer[used], data, free);
319
65
    data = (unsigned char *)data + free;
320
65
    size -= free;
321
65
    body(ctx, ctx->buffer, 64);
322
65
  }
323
324
604
  if (size >= 64) {
325
52
    data = body(ctx, data, size & ~(size_t)0x3f);
326
52
    size &= 0x3f;
327
52
  }
328
329
604
  memcpy(ctx->buffer, data, size);
330
604
}
331
332
PHPAPI void PHP_MD5Final(unsigned char *result, PHP_MD5_CTX *ctx)
333
575
{
334
575
  uint32_t used, free;
335
336
575
  used = ctx->lo & 0x3f;
337
338
575
  ctx->buffer[used++] = 0x80;
339
340
575
  free = 64 - used;
341
342
575
  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
575
  memset(&ctx->buffer[used], 0, free - 8);
350
351
575
  ctx->lo <<= 3;
352
575
  ctx->buffer[56] = ctx->lo;
353
575
  ctx->buffer[57] = ctx->lo >> 8;
354
575
  ctx->buffer[58] = ctx->lo >> 16;
355
575
  ctx->buffer[59] = ctx->lo >> 24;
356
575
  ctx->buffer[60] = ctx->hi;
357
575
  ctx->buffer[61] = ctx->hi >> 8;
358
575
  ctx->buffer[62] = ctx->hi >> 16;
359
575
  ctx->buffer[63] = ctx->hi >> 24;
360
361
575
  body(ctx, ctx->buffer, 64);
362
363
575
  result[0] = ctx->a;
364
575
  result[1] = ctx->a >> 8;
365
575
  result[2] = ctx->a >> 16;
366
575
  result[3] = ctx->a >> 24;
367
575
  result[4] = ctx->b;
368
575
  result[5] = ctx->b >> 8;
369
575
  result[6] = ctx->b >> 16;
370
575
  result[7] = ctx->b >> 24;
371
575
  result[8] = ctx->c;
372
575
  result[9] = ctx->c >> 8;
373
575
  result[10] = ctx->c >> 16;
374
575
  result[11] = ctx->c >> 24;
375
575
  result[12] = ctx->d;
376
575
  result[13] = ctx->d >> 8;
377
575
  result[14] = ctx->d >> 16;
378
575
  result[15] = ctx->d >> 24;
379
380
575
  ZEND_SECURE_ZERO(ctx, sizeof(*ctx));
381
575
}