Coverage Report

Created: 2026-07-25 06:39

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
204
{
83
204
  unsigned int i, j;
84
85
816
  for (i = 0, j = 0; j < len; i++, j += 4) {
86
612
    output[j] = (unsigned char) (input[i] & 0xff);
87
612
    output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff);
88
612
    output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff);
89
612
    output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff);
90
612
  }
91
204
}
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
18.6k
{
100
18.6k
  unsigned int i, j;
101
102
317k
  for (i = 0, j = 0; j < len; i++, j += 4)
103
298k
    output[i] = ((uint32_t) input[j]) | (((uint32_t) input[j + 1]) << 8) |
104
298k
      (((uint32_t) input[j + 2]) << 16) | (((uint32_t) input[j + 3]) << 24);
105
18.6k
}
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
896k
#define ROTL32(s,v)       (((v) << (s)) | ((v) >> (32 - (s))))
115
116
298k
#define MD4_R1(a,b,c,d,k,s)   a = ROTL32(s, a + MD4_F(b,c,d) + x[k])
117
298k
#define MD4_R2(a,b,c,d,k,s)   a = ROTL32(s, a + MD4_G(b,c,d) + x[k] + 0x5A827999)
118
298k
#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
18.6k
{
122
18.6k
  uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
123
124
18.6k
  Decode(x, block, 64);
125
126
  /* Round 1 */
127
18.6k
  MD4_R1(a,b,c,d, 0, 3);
128
18.6k
  MD4_R1(d,a,b,c, 1, 7);
129
18.6k
  MD4_R1(c,d,a,b, 2,11);
130
18.6k
  MD4_R1(b,c,d,a, 3,19);
131
18.6k
  MD4_R1(a,b,c,d, 4, 3);
132
18.6k
  MD4_R1(d,a,b,c, 5, 7);
133
18.6k
  MD4_R1(c,d,a,b, 6,11);
134
18.6k
  MD4_R1(b,c,d,a, 7,19);
135
18.6k
  MD4_R1(a,b,c,d, 8, 3);
136
18.6k
  MD4_R1(d,a,b,c, 9, 7);
137
18.6k
  MD4_R1(c,d,a,b,10,11);
138
18.6k
  MD4_R1(b,c,d,a,11,19);
139
18.6k
  MD4_R1(a,b,c,d,12, 3);
140
18.6k
  MD4_R1(d,a,b,c,13, 7);
141
18.6k
  MD4_R1(c,d,a,b,14,11);
142
18.6k
  MD4_R1(b,c,d,a,15,19);
143
144
  /* Round 2 */
145
18.6k
  MD4_R2(a,b,c,d, 0, 3);
146
18.6k
  MD4_R2(d,a,b,c, 4, 5);
147
18.6k
  MD4_R2(c,d,a,b, 8, 9);
148
18.6k
  MD4_R2(b,c,d,a,12,13);
149
18.6k
  MD4_R2(a,b,c,d, 1, 3);
150
18.6k
  MD4_R2(d,a,b,c, 5, 5);
151
18.6k
  MD4_R2(c,d,a,b, 9, 9);
152
18.6k
  MD4_R2(b,c,d,a,13,13);
153
18.6k
  MD4_R2(a,b,c,d, 2, 3);
154
18.6k
  MD4_R2(d,a,b,c, 6, 5);
155
18.6k
  MD4_R2(c,d,a,b,10, 9);
156
18.6k
  MD4_R2(b,c,d,a,14,13);
157
18.6k
  MD4_R2(a,b,c,d, 3, 3);
158
18.6k
  MD4_R2(d,a,b,c, 7, 5);
159
18.6k
  MD4_R2(c,d,a,b,11, 9);
160
18.6k
  MD4_R2(b,c,d,a,15,13);
161
162
  /* Round 3 */
163
18.6k
  MD4_R3(a,b,c,d, 0, 3);
164
18.6k
  MD4_R3(d,a,b,c, 8, 9);
165
18.6k
  MD4_R3(c,d,a,b, 4,11);
166
18.6k
  MD4_R3(b,c,d,a,12,15);
167
18.6k
  MD4_R3(a,b,c,d, 2, 3);
168
18.6k
  MD4_R3(d,a,b,c,10, 9);
169
18.6k
  MD4_R3(c,d,a,b, 6,11);
170
18.6k
  MD4_R3(b,c,d,a,14,15);
171
18.6k
  MD4_R3(a,b,c,d, 1, 3);
172
18.6k
  MD4_R3(d,a,b,c, 9, 9);
173
18.6k
  MD4_R3(c,d,a,b, 5,11);
174
18.6k
  MD4_R3(b,c,d,a,13,15);
175
18.6k
  MD4_R3(a,b,c,d, 3, 3);
176
18.6k
  MD4_R3(d,a,b,c,11, 9);
177
18.6k
  MD4_R3(c,d,a,b, 7,11);
178
18.6k
  MD4_R3(b,c,d,a,15,15);
179
180
18.6k
  state[0] += a;
181
18.6k
  state[1] += b;
182
18.6k
  state[2] += c;
183
18.6k
  state[3] += d;
184
18.6k
}
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
103
{
191
103
  context->count[0] = context->count[1] = 0;
192
  /* Load magic initialization constants.
193
   */
194
103
  context->state[0] = 0x67452301;
195
103
  context->state[1] = 0xefcdab89;
196
103
  context->state[2] = 0x98badcfe;
197
103
  context->state[3] = 0x10325476;
198
103
}
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
306
{
208
306
  unsigned int index, partLen;
209
306
  size_t i;
210
211
  /* Compute number of bytes mod 64 */
212
306
  index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
213
214
  /* Update number of bits */
215
306
  if ((context->count[0] += ((uint32_t) inputLen << 3))
216
306
    < ((uint32_t) inputLen << 3))
217
10
    context->count[1]++;
218
306
  context->count[1] += (uint32_t) (inputLen >> 29);
219
220
306
  partLen = 64 - index;
221
222
  /* Transform as many times as possible.
223
   */
224
306
  if (inputLen >= partLen) {
225
182
    memcpy((unsigned char*) & context->buffer[index], (unsigned char*) input, partLen);
226
182
    MD4Transform(context->state, context->buffer);
227
228
18.6k
    for (i = partLen; i + 63 < inputLen; i += 64) {
229
18.4k
      MD4Transform(context->state, &input[i]);
230
18.4k
    }
231
232
182
    index = 0;
233
182
  } else {
234
124
    i = 0;
235
124
  }
236
237
  /* Buffer remaining input */
238
306
  memcpy((unsigned char*) & context->buffer[index], (unsigned char*) & input[i], inputLen - i);
239
306
}
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
102
{
248
102
  unsigned char bits[8];
249
102
  unsigned int index, padLen;
250
251
  /* Save number of bits */
252
102
  Encode(bits, context->count, 8);
253
254
  /* Pad out to 56 mod 64.
255
   */
256
102
  index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
257
102
  padLen = (index < 56) ? (56 - index) : (120 - index);
258
102
  PHP_MD4Update(context, PADDING, padLen);
259
260
  /* Append length (before padding) */
261
102
  PHP_MD4Update(context, bits, 8);
262
263
  /* Store state in digest */
264
102
  Encode(digest, context->state, 16);
265
266
  /* Zeroize sensitive information.
267
   */
268
102
  ZEND_SECURE_ZERO((unsigned char*) context, sizeof(*context));
269
102
}
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
44
{
294
44
  memset(context, 0, sizeof(PHP_MD2_CTX));
295
44
}
296
297
static void MD2_Transform(PHP_MD2_CTX *context, const unsigned char *block)
298
2.23k
{
299
2.23k
  unsigned char i,j,t = 0;
300
301
38.0k
  for(i = 0; i < 16; i++) {
302
35.8k
    context->state[16+i] = block[i];
303
35.8k
    context->state[32+i] = (context->state[16+i] ^ context->state[i]);
304
35.8k
  }
305
306
42.5k
  for(i = 0; i < 18; i++) {
307
1.97M
    for(j = 0; j < 48; j++) {
308
1.93M
      t = context->state[j] = context->state[j] ^ MD2_S[t];
309
1.93M
    }
310
40.3k
    t += i;
311
40.3k
  }
312
313
  /* Update checksum -- must be after transform to avoid fouling up last message block */
314
2.23k
  t = context->checksum[15];
315
38.0k
  for(i = 0; i < 16; i++) {
316
35.8k
    t = context->checksum[i] ^= MD2_S[block[i] ^ t];
317
35.8k
  }
318
2.23k
}
319
320
PHP_HASH_API void PHP_MD2Update(PHP_MD2_CTX *context, const unsigned char *buf, size_t len)
321
26
{
322
26
  const unsigned char *p = buf, *e = buf + len;
323
324
26
  if (context->in_buffer) {
325
3
    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
2
    memcpy(context->buffer + context->in_buffer, p, 16 - context->in_buffer);
333
2
    MD2_Transform(context, context->buffer);
334
2
    p += 16 - context->in_buffer;
335
2
    context->in_buffer = 0;
336
2
  }
337
338
  /* Process as many whole blocks as remain */
339
2.21k
  while ((p + 16) <= e) {
340
2.18k
    MD2_Transform(context, p);
341
2.18k
    p += 16;
342
2.18k
  }
343
344
  /* Copy remaining data to buffer */
345
25
  if (p < e) {
346
4
    memcpy(context->buffer, p, e - p);
347
4
    context->in_buffer = (char) (e - p);
348
4
  }
349
25
}
350
351
PHP_HASH_API void PHP_MD2Final(unsigned char output[16], PHP_MD2_CTX *context)
352
26
{
353
26
  memset(context->buffer + context->in_buffer, 16 - context->in_buffer, 16 - context->in_buffer);
354
26
  MD2_Transform(context, context->buffer);
355
26
  MD2_Transform(context, context->checksum);
356
357
26
  memcpy(output, context->state, 16);
358
26
}
359
360
static hash_spec_result php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
361
44
{
362
44
  PHP_MD2_CTX *ctx = (PHP_MD2_CTX *) hash->context;
363
44
  hash_spec_result r = HASH_SPEC_FAILURE;
364
44
  if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
365
32
    && (r = php_hash_unserialize_spec(hash, zv, PHP_MD2_SPEC)) == HASH_SPEC_SUCCESS
366
27
    && (unsigned char) ctx->in_buffer < sizeof(ctx->buffer)) {
367
26
    return HASH_SPEC_SUCCESS;
368
26
  }
369
370
18
    return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
371
44
}