Coverage Report

Created: 2026-06-02 06:37

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