Coverage Report

Created: 2025-12-31 07:28

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