Coverage Report

Created: 2026-10-28 06:20

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