Coverage Report

Created: 2026-09-14 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/hash/hash_md.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
  | Taken from: ext/standard/md5.c                                       |
12
  +----------------------------------------------------------------------+
13
*/
14
15
#include "php_hash.h"
16
#include "php_hash_md.h"
17
18
const php_hash_ops php_hash_md5_ops = {
19
  "md5",
20
  (php_hash_init_func_t) PHP_MD5InitArgs,
21
  (php_hash_update_func_t) PHP_MD5Update,
22
  (php_hash_final_func_t) PHP_MD5Final,
23
  php_hash_copy,
24
  php_hash_serialize,
25
  php_hash_unserialize,
26
  PHP_MD5_SPEC,
27
  16,
28
  64,
29
  sizeof(PHP_MD5_CTX),
30
  1,
31
  0
32
};
33
34
const php_hash_ops php_hash_md4_ops = {
35
  "md4",
36
  (php_hash_init_func_t) PHP_MD4InitArgs,
37
  (php_hash_update_func_t) PHP_MD4Update,
38
  (php_hash_final_func_t) PHP_MD4Final,
39
  php_hash_copy,
40
  php_hash_serialize,
41
  php_hash_unserialize,
42
  PHP_MD4_SPEC,
43
  16,
44
  64,
45
  sizeof(PHP_MD4_CTX),
46
  1,
47
  0
48
};
49
50
static hash_spec_result php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv);
51
52
const php_hash_ops php_hash_md2_ops = {
53
  "md2",
54
  (php_hash_init_func_t) PHP_MD2InitArgs,
55
  (php_hash_update_func_t) PHP_MD2Update,
56
  (php_hash_final_func_t) PHP_MD2Final,
57
  php_hash_copy,
58
  php_hash_serialize,
59
  php_md2_unserialize,
60
  PHP_MD2_SPEC,
61
  16,
62
  16,
63
  sizeof(PHP_MD2_CTX),
64
  1,
65
  0
66
};
67
68
/* MD common stuff */
69
70
static const unsigned char PADDING[64] =
71
{
72
  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
73
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
74
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
75
};
76
77
/* {{{ Encode
78
   Encodes input (uint32_t) into output (unsigned char). Assumes len is
79
   a multiple of 4.
80
 */
81
static void Encode(unsigned char *output, uint32_t *input, unsigned int len)
82
172
{
83
172
  unsigned int i, j;
84
85
688
  for (i = 0, j = 0; j < len; i++, j += 4) {
86
516
    output[j] = (unsigned char) (input[i] & 0xff);
87
516
    output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff);
88
516
    output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff);
89
516
    output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff);
90
516
  }
91
172
}
92
/* }}} */
93
94
/* {{{ Decode
95
   Decodes input (unsigned char) into output (uint32_t). Assumes len is
96
   a multiple of 4.
97
 */
98
static void Decode(uint32_t *output, const unsigned char *input, unsigned int len)
99
21.7k
{
100
21.7k
  unsigned int i, j;
101
102
370k
  for (i = 0, j = 0; j < len; i++, j += 4)
103
348k
    output[i] = ((uint32_t) input[j]) | (((uint32_t) input[j + 1]) << 8) |
104
348k
      (((uint32_t) input[j + 2]) << 16) | (((uint32_t) input[j + 3]) << 24);
105
21.7k
}
106
/* }}} */
107
108
/* MD4 */
109
110
#define MD4_F(x,y,z)      ((z) ^ ((x) & ((y) ^ (z))))
111
#define MD4_G(x,y,z)      (((x) & ((y) | (z))) | ((y) & (z)))
112
#define MD4_H(x,y,z)      ((x) ^ (y) ^ (z))
113
114
1.04M
#define ROTL32(s,v)       (((v) << (s)) | ((v) >> (32 - (s))))
115
116
348k
#define MD4_R1(a,b,c,d,k,s)   a = ROTL32(s, a + MD4_F(b,c,d) + x[k])
117
348k
#define MD4_R2(a,b,c,d,k,s)   a = ROTL32(s, a + MD4_G(b,c,d) + x[k] + 0x5A827999)
118
348k
#define MD4_R3(a,b,c,d,k,s)   a = ROTL32(s, a + MD4_H(b,c,d) + x[k] + 0x6ED9EBA1)
119
120
static void MD4Transform(uint32_t state[4], const unsigned char block[64])
121
21.7k
{
122
21.7k
  uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
123
124
21.7k
  Decode(x, block, 64);
125
126
  /* Round 1 */
127
21.7k
  MD4_R1(a,b,c,d, 0, 3);
128
21.7k
  MD4_R1(d,a,b,c, 1, 7);
129
21.7k
  MD4_R1(c,d,a,b, 2,11);
130
21.7k
  MD4_R1(b,c,d,a, 3,19);
131
21.7k
  MD4_R1(a,b,c,d, 4, 3);
132
21.7k
  MD4_R1(d,a,b,c, 5, 7);
133
21.7k
  MD4_R1(c,d,a,b, 6,11);
134
21.7k
  MD4_R1(b,c,d,a, 7,19);
135
21.7k
  MD4_R1(a,b,c,d, 8, 3);
136
21.7k
  MD4_R1(d,a,b,c, 9, 7);
137
21.7k
  MD4_R1(c,d,a,b,10,11);
138
21.7k
  MD4_R1(b,c,d,a,11,19);
139
21.7k
  MD4_R1(a,b,c,d,12, 3);
140
21.7k
  MD4_R1(d,a,b,c,13, 7);
141
21.7k
  MD4_R1(c,d,a,b,14,11);
142
21.7k
  MD4_R1(b,c,d,a,15,19);
143
144
  /* Round 2 */
145
21.7k
  MD4_R2(a,b,c,d, 0, 3);
146
21.7k
  MD4_R2(d,a,b,c, 4, 5);
147
21.7k
  MD4_R2(c,d,a,b, 8, 9);
148
21.7k
  MD4_R2(b,c,d,a,12,13);
149
21.7k
  MD4_R2(a,b,c,d, 1, 3);
150
21.7k
  MD4_R2(d,a,b,c, 5, 5);
151
21.7k
  MD4_R2(c,d,a,b, 9, 9);
152
21.7k
  MD4_R2(b,c,d,a,13,13);
153
21.7k
  MD4_R2(a,b,c,d, 2, 3);
154
21.7k
  MD4_R2(d,a,b,c, 6, 5);
155
21.7k
  MD4_R2(c,d,a,b,10, 9);
156
21.7k
  MD4_R2(b,c,d,a,14,13);
157
21.7k
  MD4_R2(a,b,c,d, 3, 3);
158
21.7k
  MD4_R2(d,a,b,c, 7, 5);
159
21.7k
  MD4_R2(c,d,a,b,11, 9);
160
21.7k
  MD4_R2(b,c,d,a,15,13);
161
162
  /* Round 3 */
163
21.7k
  MD4_R3(a,b,c,d, 0, 3);
164
21.7k
  MD4_R3(d,a,b,c, 8, 9);
165
21.7k
  MD4_R3(c,d,a,b, 4,11);
166
21.7k
  MD4_R3(b,c,d,a,12,15);
167
21.7k
  MD4_R3(a,b,c,d, 2, 3);
168
21.7k
  MD4_R3(d,a,b,c,10, 9);
169
21.7k
  MD4_R3(c,d,a,b, 6,11);
170
21.7k
  MD4_R3(b,c,d,a,14,15);
171
21.7k
  MD4_R3(a,b,c,d, 1, 3);
172
21.7k
  MD4_R3(d,a,b,c, 9, 9);
173
21.7k
  MD4_R3(c,d,a,b, 5,11);
174
21.7k
  MD4_R3(b,c,d,a,13,15);
175
21.7k
  MD4_R3(a,b,c,d, 3, 3);
176
21.7k
  MD4_R3(d,a,b,c,11, 9);
177
21.7k
  MD4_R3(c,d,a,b, 7,11);
178
21.7k
  MD4_R3(b,c,d,a,15,15);
179
180
21.7k
  state[0] += a;
181
21.7k
  state[1] += b;
182
21.7k
  state[2] += c;
183
21.7k
  state[3] += d;
184
21.7k
}
185
186
/* {{{ PHP_MD4InitArgs
187
 * MD4 initialization. Begins an MD4 operation, writing a new context.
188
 */
189
PHP_HASH_API void PHP_MD4InitArgs(PHP_MD4_CTX * context, ZEND_ATTRIBUTE_UNUSED HashTable *args)
190
90
{
191
90
  context->count[0] = context->count[1] = 0;
192
  /* Load magic initialization constants.
193
   */
194
90
  context->state[0] = 0x67452301;
195
90
  context->state[1] = 0xefcdab89;
196
90
  context->state[2] = 0x98badcfe;
197
90
  context->state[3] = 0x10325476;
198
90
}
199
/* }}} */
200
201
/* {{{ PHP_MD4Update
202
   MD4 block update operation. Continues an MD4 message-digest
203
   operation, processing another message block, and updating the
204
   context.
205
 */
206
PHP_HASH_API void PHP_MD4Update(PHP_MD4_CTX * context, const unsigned char *input, size_t inputLen)
207
258
{
208
258
  unsigned int index, partLen;
209
258
  size_t i;
210
211
  /* Compute number of bytes mod 64 */
212
258
  index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
213
214
  /* Update number of bits */
215
258
  if ((context->count[0] += ((uint32_t) inputLen << 3))
216
258
    < ((uint32_t) inputLen << 3))
217
8
    context->count[1]++;
218
258
  context->count[1] += (uint32_t) (inputLen >> 29);
219
220
258
  partLen = 64 - index;
221
222
  /* Transform as many times as possible.
223
   */
224
258
  if (inputLen >= partLen) {
225
164
    memcpy((unsigned char*) & context->buffer[index], (unsigned char*) input, partLen);
226
164
    MD4Transform(context->state, context->buffer);
227
228
21.7k
    for (i = partLen; i + 63 < inputLen; i += 64) {
229
21.6k
      MD4Transform(context->state, &input[i]);
230
21.6k
    }
231
232
164
    index = 0;
233
164
  } else {
234
94
    i = 0;
235
94
  }
236
237
  /* Buffer remaining input */
238
258
  memcpy((unsigned char*) & context->buffer[index], (unsigned char*) & input[i], inputLen - i);
239
258
}
240
/* }}} */
241
242
/* {{{ PHP_MD4Final
243
   MD4 finalization. Ends an MD4 message-digest operation, writing
244
   the message digest and zeroizing the context.
245
 */
246
PHP_HASH_API void PHP_MD4Final(unsigned char digest[16], PHP_MD4_CTX * context)
247
86
{
248
86
  unsigned char bits[8];
249
86
  unsigned int index, padLen;
250
251
  /* Save number of bits */
252
86
  Encode(bits, context->count, 8);
253
254
  /* Pad out to 56 mod 64.
255
   */
256
86
  index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
257
86
  padLen = (index < 56) ? (56 - index) : (120 - index);
258
86
  PHP_MD4Update(context, PADDING, padLen);
259
260
  /* Append length (before padding) */
261
86
  PHP_MD4Update(context, bits, 8);
262
263
  /* Store state in digest */
264
86
  Encode(digest, context->state, 16);
265
266
  /* Zeroize sensitive information.
267
   */
268
86
  ZEND_SECURE_ZERO((unsigned char*) context, sizeof(*context));
269
86
}
270
/* }}} */
271
272
/* MD2 */
273
274
static const unsigned char MD2_S[256] = {
275
   41,  46,  67, 201, 162, 216, 124,   1,  61,  54,  84, 161, 236, 240,   6,  19,
276
   98, 167,   5, 243, 192, 199, 115, 140, 152, 147,  43, 217, 188,  76, 130, 202,
277
   30, 155,  87,  60, 253, 212, 224,  22, 103,  66, 111,  24, 138,  23, 229,  18,
278
  190,  78, 196, 214, 218, 158, 222,  73, 160, 251, 245, 142, 187,  47, 238, 122,
279
  169, 104, 121, 145,  21, 178,   7,  63, 148, 194,  16, 137,  11,  34,  95,  33,
280
  128, 127,  93, 154,  90, 144,  50,  39,  53,  62, 204, 231, 191, 247, 151,   3,
281
  255,  25,  48, 179,  72, 165, 181, 209, 215,  94, 146,  42, 172,  86, 170, 198,
282
   79, 184,  56, 210, 150, 164, 125, 182, 118, 252, 107, 226, 156, 116,   4, 241,
283
   69, 157, 112,  89, 100, 113, 135,  32, 134,  91, 207, 101, 230,  45, 168,   2,
284
   27,  96,  37, 173, 174, 176, 185, 246,  28,  70,  97, 105,  52,  64, 126,  15,
285
   85,  71, 163,  35, 221,  81, 175,  58, 195,  92, 249, 206, 186, 197, 234,  38,
286
   44,  83,  13, 110, 133,  40, 132,   9, 211, 223, 205, 244,  65, 129,  77,  82,
287
  106, 220,  55, 200, 108, 193, 171, 250,  36, 225, 123,   8,  12, 189, 177,  74,
288
  120, 136, 149, 139, 227,  99, 232, 109, 233, 203, 213, 254,  59,   0,  29,  57,
289
  242, 239, 183,  14, 102,  88, 208, 228, 166, 119, 114, 248, 235, 117,  75,  10,
290
   49,  68,  80, 180, 143, 237,  31,  26, 219, 153, 141,  51, 159,  17, 131,  20 };
291
292
PHP_HASH_API void PHP_MD2InitArgs(PHP_MD2_CTX *context, ZEND_ATTRIBUTE_UNUSED HashTable *args)
293
42
{
294
42
  memset(context, 0, sizeof(PHP_MD2_CTX));
295
42
}
296
297
static void MD2_Transform(PHP_MD2_CTX *context, const unsigned char *block)
298
2.47k
{
299
2.47k
  unsigned char i,j,t = 0;
300
301
42.0k
  for(i = 0; i < 16; i++) {
302
39.6k
    context->state[16+i] = block[i];
303
39.6k
    context->state[32+i] = (context->state[16+i] ^ context->state[i]);
304
39.6k
  }
305
306
47.0k
  for(i = 0; i < 18; i++) {
307
2.18M
    for(j = 0; j < 48; j++) {
308
2.13M
      t = context->state[j] = context->state[j] ^ MD2_S[t];
309
2.13M
    }
310
44.5k
    t += i;
311
44.5k
  }
312
313
  /* Update checksum -- must be after transform to avoid fouling up last message block */
314
2.47k
  t = context->checksum[15];
315
42.0k
  for(i = 0; i < 16; i++) {
316
39.6k
    t = context->checksum[i] ^= MD2_S[block[i] ^ t];
317
39.6k
  }
318
2.47k
}
319
320
PHP_HASH_API void PHP_MD2Update(PHP_MD2_CTX *context, const unsigned char *buf, size_t len)
321
28
{
322
28
  const unsigned char *p = buf, *e = buf + len;
323
324
28
  if (context->in_buffer) {
325
5
    if (context->in_buffer + len < 16) {
326
      /* Not enough for block, just pass into buffer */
327
1
      memcpy(context->buffer + context->in_buffer, p, len);
328
1
      context->in_buffer += (char) len;
329
1
      return;
330
1
    }
331
    /* Put buffered data together with inbound for a single block */
332
4
    memcpy(context->buffer + context->in_buffer, p, 16 - context->in_buffer);
333
4
    MD2_Transform(context, context->buffer);
334
4
    p += 16 - context->in_buffer;
335
4
    context->in_buffer = 0;
336
4
  }
337
338
  /* Process as many whole blocks as remain */
339
2.44k
  while ((p + 16) <= e) {
340
2.41k
    MD2_Transform(context, p);
341
2.41k
    p += 16;
342
2.41k
  }
343
344
  /* Copy remaining data to buffer */
345
27
  if (p < e) {
346
11
    memcpy(context->buffer, p, e - p);
347
11
    context->in_buffer = (char) (e - p);
348
11
  }
349
27
}
350
351
PHP_HASH_API void PHP_MD2Final(unsigned char output[16], PHP_MD2_CTX *context)
352
28
{
353
28
  memset(context->buffer + context->in_buffer, 16 - context->in_buffer, 16 - context->in_buffer);
354
28
  MD2_Transform(context, context->buffer);
355
28
  MD2_Transform(context, context->checksum);
356
357
28
  memcpy(output, context->state, 16);
358
28
}
359
360
static hash_spec_result php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
361
42
{
362
42
  PHP_MD2_CTX *ctx = (PHP_MD2_CTX *) hash->context;
363
42
  hash_spec_result r = HASH_SPEC_FAILURE;
364
42
  if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
365
34
    && (r = php_hash_unserialize_spec(hash, zv, PHP_MD2_SPEC)) == HASH_SPEC_SUCCESS
366
29
    && (unsigned char) ctx->in_buffer < sizeof(ctx->buffer)) {
367
28
    return HASH_SPEC_SUCCESS;
368
28
  }
369
370
14
    return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
371
42
}