Coverage Report

Created: 2026-06-02 06:37

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
30.5k
{
97
30.5k
  unsigned int i, j;
98
99
518k
  for (i = 0, j = 0; j < len; i++, j += 4)
100
488k
    output[i] = ((uint32_t) input[j]) | (((uint32_t) input[j + 1]) << 8) |
101
488k
      (((uint32_t) input[j + 2]) << 16) | (((uint32_t) input[j + 3]) << 24);
102
30.5k
}
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.46M
#define ROTL32(s,v)       (((v) << (s)) | ((v) >> (32 - (s))))
112
113
488k
#define MD4_R1(a,b,c,d,k,s)   a = ROTL32(s, a + MD4_F(b,c,d) + x[k])
114
488k
#define MD4_R2(a,b,c,d,k,s)   a = ROTL32(s, a + MD4_G(b,c,d) + x[k] + 0x5A827999)
115
488k
#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
30.5k
{
119
30.5k
  uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
120
121
30.5k
  Decode(x, block, 64);
122
123
  /* Round 1 */
124
30.5k
  MD4_R1(a,b,c,d, 0, 3);
125
30.5k
  MD4_R1(d,a,b,c, 1, 7);
126
30.5k
  MD4_R1(c,d,a,b, 2,11);
127
30.5k
  MD4_R1(b,c,d,a, 3,19);
128
30.5k
  MD4_R1(a,b,c,d, 4, 3);
129
30.5k
  MD4_R1(d,a,b,c, 5, 7);
130
30.5k
  MD4_R1(c,d,a,b, 6,11);
131
30.5k
  MD4_R1(b,c,d,a, 7,19);
132
30.5k
  MD4_R1(a,b,c,d, 8, 3);
133
30.5k
  MD4_R1(d,a,b,c, 9, 7);
134
30.5k
  MD4_R1(c,d,a,b,10,11);
135
30.5k
  MD4_R1(b,c,d,a,11,19);
136
30.5k
  MD4_R1(a,b,c,d,12, 3);
137
30.5k
  MD4_R1(d,a,b,c,13, 7);
138
30.5k
  MD4_R1(c,d,a,b,14,11);
139
30.5k
  MD4_R1(b,c,d,a,15,19);
140
141
  /* Round 2 */
142
30.5k
  MD4_R2(a,b,c,d, 0, 3);
143
30.5k
  MD4_R2(d,a,b,c, 4, 5);
144
30.5k
  MD4_R2(c,d,a,b, 8, 9);
145
30.5k
  MD4_R2(b,c,d,a,12,13);
146
30.5k
  MD4_R2(a,b,c,d, 1, 3);
147
30.5k
  MD4_R2(d,a,b,c, 5, 5);
148
30.5k
  MD4_R2(c,d,a,b, 9, 9);
149
30.5k
  MD4_R2(b,c,d,a,13,13);
150
30.5k
  MD4_R2(a,b,c,d, 2, 3);
151
30.5k
  MD4_R2(d,a,b,c, 6, 5);
152
30.5k
  MD4_R2(c,d,a,b,10, 9);
153
30.5k
  MD4_R2(b,c,d,a,14,13);
154
30.5k
  MD4_R2(a,b,c,d, 3, 3);
155
30.5k
  MD4_R2(d,a,b,c, 7, 5);
156
30.5k
  MD4_R2(c,d,a,b,11, 9);
157
30.5k
  MD4_R2(b,c,d,a,15,13);
158
159
  /* Round 3 */
160
30.5k
  MD4_R3(a,b,c,d, 0, 3);
161
30.5k
  MD4_R3(d,a,b,c, 8, 9);
162
30.5k
  MD4_R3(c,d,a,b, 4,11);
163
30.5k
  MD4_R3(b,c,d,a,12,15);
164
30.5k
  MD4_R3(a,b,c,d, 2, 3);
165
30.5k
  MD4_R3(d,a,b,c,10, 9);
166
30.5k
  MD4_R3(c,d,a,b, 6,11);
167
30.5k
  MD4_R3(b,c,d,a,14,15);
168
30.5k
  MD4_R3(a,b,c,d, 1, 3);
169
30.5k
  MD4_R3(d,a,b,c, 9, 9);
170
30.5k
  MD4_R3(c,d,a,b, 5,11);
171
30.5k
  MD4_R3(b,c,d,a,13,15);
172
30.5k
  MD4_R3(a,b,c,d, 3, 3);
173
30.5k
  MD4_R3(d,a,b,c,11, 9);
174
30.5k
  MD4_R3(c,d,a,b, 7,11);
175
30.5k
  MD4_R3(b,c,d,a,15,15);
176
177
30.5k
  state[0] += a;
178
30.5k
  state[1] += b;
179
30.5k
  state[2] += c;
180
30.5k
  state[3] += d;
181
30.5k
}
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
69
{
188
69
  context->count[0] = context->count[1] = 0;
189
  /* Load magic initialization constants.
190
   */
191
69
  context->state[0] = 0x67452301;
192
69
  context->state[1] = 0xefcdab89;
193
69
  context->state[2] = 0x98badcfe;
194
69
  context->state[3] = 0x10325476;
195
69
}
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
6
    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
129
    memcpy((unsigned char*) & context->buffer[index], (unsigned char*) input, partLen);
223
129
    MD4Transform(context->state, context->buffer);
224
225
30.5k
    for (i = partLen; i + 63 < inputLen; i += 64) {
226
30.3k
      MD4Transform(context->state, &input[i]);
227
30.3k
    }
228
229
129
    index = 0;
230
129
  } else {
231
72
    i = 0;
232
72
  }
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
2.22k
{
296
2.22k
  unsigned char i,j,t = 0;
297
298
37.8k
  for(i = 0; i < 16; i++) {
299
35.6k
    context->state[16+i] = block[i];
300
35.6k
    context->state[32+i] = (context->state[16+i] ^ context->state[i]);
301
35.6k
  }
302
303
42.3k
  for(i = 0; i < 18; i++) {
304
1.96M
    for(j = 0; j < 48; j++) {
305
1.92M
      t = context->state[j] = context->state[j] ^ MD2_S[t];
306
1.92M
    }
307
40.1k
    t += i;
308
40.1k
  }
309
310
  /* Update checksum -- must be after transform to avoid fouling up last message block */
311
2.22k
  t = context->checksum[15];
312
37.8k
  for(i = 0; i < 16; i++) {
313
35.6k
    t = context->checksum[i] ^= MD2_S[block[i] ^ t];
314
35.6k
  }
315
2.22k
}
316
317
PHP_HASH_API void PHP_MD2Update(PHP_MD2_CTX *context, const unsigned char *buf, size_t len)
318
23
{
319
23
  const unsigned char *p = buf, *e = buf + len;
320
321
23
  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
2.20k
  while ((p + 16) <= e) {
337
2.18k
    MD2_Transform(context, p);
338
2.18k
    p += 16;
339
2.18k
  }
340
341
  /* Copy remaining data to buffer */
342
22
  if (p < e) {
343
12
    memcpy(context->buffer, p, e - p);
344
12
    context->in_buffer = (char) (e - p);
345
12
  }
346
22
}
347
348
PHP_HASH_API void PHP_MD2Final(unsigned char output[16], PHP_MD2_CTX *context)
349
23
{
350
23
  memset(context->buffer + context->in_buffer, 16 - context->in_buffer, 16 - context->in_buffer);
351
23
  MD2_Transform(context, context->buffer);
352
23
  MD2_Transform(context, context->checksum);
353
354
23
  memcpy(output, context->state, 16);
355
23
}
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
29
    && (r = php_hash_unserialize_spec(hash, zv, PHP_MD2_SPEC)) == HASH_SPEC_SUCCESS
363
24
    && (unsigned char) ctx->in_buffer < sizeof(ctx->buffer)) {
364
23
    return HASH_SPEC_SUCCESS;
365
23
  }
366
367
8
    return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
368
31
}