Coverage Report

Created: 2026-06-13 07:01

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