Coverage Report

Created: 2026-06-02 06:40

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