Coverage Report

Created: 2026-04-01 06:49

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