Coverage Report

Created: 2026-09-14 06:25

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