PolarSSL v1.2.5
md5.c
Go to the documentation of this file.
1 /*
2  * RFC 1321 compliant MD5 implementation
3  *
4  * Copyright (C) 2006-2010, Brainspark B.V.
5  *
6  * This file is part of PolarSSL (http://www.polarssl.org)
7  * Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
8  *
9  * All rights reserved.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25 /*
26  * The MD5 algorithm was designed by Ron Rivest in 1991.
27  *
28  * http://www.ietf.org/rfc/rfc1321.txt
29  */
30 
31 #include "polarssl/config.h"
32 
33 #if defined(POLARSSL_MD5_C)
34 
35 #include "polarssl/md5.h"
36 
37 #if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST)
38 #include <stdio.h>
39 #endif
40 
41 /*
42  * 32-bit integer manipulation macros (little endian)
43  */
44 #ifndef GET_UINT32_LE
45 #define GET_UINT32_LE(n,b,i) \
46 { \
47  (n) = ( (uint32_t) (b)[(i) ] ) \
48  | ( (uint32_t) (b)[(i) + 1] << 8 ) \
49  | ( (uint32_t) (b)[(i) + 2] << 16 ) \
50  | ( (uint32_t) (b)[(i) + 3] << 24 ); \
51 }
52 #endif
53 
54 #ifndef PUT_UINT32_LE
55 #define PUT_UINT32_LE(n,b,i) \
56 { \
57  (b)[(i) ] = (unsigned char) ( (n) ); \
58  (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \
59  (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \
60  (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \
61 }
62 #endif
63 
64 /*
65  * MD5 context setup
66  */
67 void md5_starts( md5_context *ctx )
68 {
69  ctx->total[0] = 0;
70  ctx->total[1] = 0;
71 
72  ctx->state[0] = 0x67452301;
73  ctx->state[1] = 0xEFCDAB89;
74  ctx->state[2] = 0x98BADCFE;
75  ctx->state[3] = 0x10325476;
76 }
77 
78 static void md5_process( md5_context *ctx, const unsigned char data[64] )
79 {
80  uint32_t X[16], A, B, C, D;
81 
82  GET_UINT32_LE( X[ 0], data, 0 );
83  GET_UINT32_LE( X[ 1], data, 4 );
84  GET_UINT32_LE( X[ 2], data, 8 );
85  GET_UINT32_LE( X[ 3], data, 12 );
86  GET_UINT32_LE( X[ 4], data, 16 );
87  GET_UINT32_LE( X[ 5], data, 20 );
88  GET_UINT32_LE( X[ 6], data, 24 );
89  GET_UINT32_LE( X[ 7], data, 28 );
90  GET_UINT32_LE( X[ 8], data, 32 );
91  GET_UINT32_LE( X[ 9], data, 36 );
92  GET_UINT32_LE( X[10], data, 40 );
93  GET_UINT32_LE( X[11], data, 44 );
94  GET_UINT32_LE( X[12], data, 48 );
95  GET_UINT32_LE( X[13], data, 52 );
96  GET_UINT32_LE( X[14], data, 56 );
97  GET_UINT32_LE( X[15], data, 60 );
98 
99 #define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
100 
101 #define P(a,b,c,d,k,s,t) \
102 { \
103  a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
104 }
105 
106  A = ctx->state[0];
107  B = ctx->state[1];
108  C = ctx->state[2];
109  D = ctx->state[3];
110 
111 #define F(x,y,z) (z ^ (x & (y ^ z)))
112 
113  P( A, B, C, D, 0, 7, 0xD76AA478 );
114  P( D, A, B, C, 1, 12, 0xE8C7B756 );
115  P( C, D, A, B, 2, 17, 0x242070DB );
116  P( B, C, D, A, 3, 22, 0xC1BDCEEE );
117  P( A, B, C, D, 4, 7, 0xF57C0FAF );
118  P( D, A, B, C, 5, 12, 0x4787C62A );
119  P( C, D, A, B, 6, 17, 0xA8304613 );
120  P( B, C, D, A, 7, 22, 0xFD469501 );
121  P( A, B, C, D, 8, 7, 0x698098D8 );
122  P( D, A, B, C, 9, 12, 0x8B44F7AF );
123  P( C, D, A, B, 10, 17, 0xFFFF5BB1 );
124  P( B, C, D, A, 11, 22, 0x895CD7BE );
125  P( A, B, C, D, 12, 7, 0x6B901122 );
126  P( D, A, B, C, 13, 12, 0xFD987193 );
127  P( C, D, A, B, 14, 17, 0xA679438E );
128  P( B, C, D, A, 15, 22, 0x49B40821 );
129 
130 #undef F
131 
132 #define F(x,y,z) (y ^ (z & (x ^ y)))
133 
134  P( A, B, C, D, 1, 5, 0xF61E2562 );
135  P( D, A, B, C, 6, 9, 0xC040B340 );
136  P( C, D, A, B, 11, 14, 0x265E5A51 );
137  P( B, C, D, A, 0, 20, 0xE9B6C7AA );
138  P( A, B, C, D, 5, 5, 0xD62F105D );
139  P( D, A, B, C, 10, 9, 0x02441453 );
140  P( C, D, A, B, 15, 14, 0xD8A1E681 );
141  P( B, C, D, A, 4, 20, 0xE7D3FBC8 );
142  P( A, B, C, D, 9, 5, 0x21E1CDE6 );
143  P( D, A, B, C, 14, 9, 0xC33707D6 );
144  P( C, D, A, B, 3, 14, 0xF4D50D87 );
145  P( B, C, D, A, 8, 20, 0x455A14ED );
146  P( A, B, C, D, 13, 5, 0xA9E3E905 );
147  P( D, A, B, C, 2, 9, 0xFCEFA3F8 );
148  P( C, D, A, B, 7, 14, 0x676F02D9 );
149  P( B, C, D, A, 12, 20, 0x8D2A4C8A );
150 
151 #undef F
152 
153 #define F(x,y,z) (x ^ y ^ z)
154 
155  P( A, B, C, D, 5, 4, 0xFFFA3942 );
156  P( D, A, B, C, 8, 11, 0x8771F681 );
157  P( C, D, A, B, 11, 16, 0x6D9D6122 );
158  P( B, C, D, A, 14, 23, 0xFDE5380C );
159  P( A, B, C, D, 1, 4, 0xA4BEEA44 );
160  P( D, A, B, C, 4, 11, 0x4BDECFA9 );
161  P( C, D, A, B, 7, 16, 0xF6BB4B60 );
162  P( B, C, D, A, 10, 23, 0xBEBFBC70 );
163  P( A, B, C, D, 13, 4, 0x289B7EC6 );
164  P( D, A, B, C, 0, 11, 0xEAA127FA );
165  P( C, D, A, B, 3, 16, 0xD4EF3085 );
166  P( B, C, D, A, 6, 23, 0x04881D05 );
167  P( A, B, C, D, 9, 4, 0xD9D4D039 );
168  P( D, A, B, C, 12, 11, 0xE6DB99E5 );
169  P( C, D, A, B, 15, 16, 0x1FA27CF8 );
170  P( B, C, D, A, 2, 23, 0xC4AC5665 );
171 
172 #undef F
173 
174 #define F(x,y,z) (y ^ (x | ~z))
175 
176  P( A, B, C, D, 0, 6, 0xF4292244 );
177  P( D, A, B, C, 7, 10, 0x432AFF97 );
178  P( C, D, A, B, 14, 15, 0xAB9423A7 );
179  P( B, C, D, A, 5, 21, 0xFC93A039 );
180  P( A, B, C, D, 12, 6, 0x655B59C3 );
181  P( D, A, B, C, 3, 10, 0x8F0CCC92 );
182  P( C, D, A, B, 10, 15, 0xFFEFF47D );
183  P( B, C, D, A, 1, 21, 0x85845DD1 );
184  P( A, B, C, D, 8, 6, 0x6FA87E4F );
185  P( D, A, B, C, 15, 10, 0xFE2CE6E0 );
186  P( C, D, A, B, 6, 15, 0xA3014314 );
187  P( B, C, D, A, 13, 21, 0x4E0811A1 );
188  P( A, B, C, D, 4, 6, 0xF7537E82 );
189  P( D, A, B, C, 11, 10, 0xBD3AF235 );
190  P( C, D, A, B, 2, 15, 0x2AD7D2BB );
191  P( B, C, D, A, 9, 21, 0xEB86D391 );
192 
193 #undef F
194 
195  ctx->state[0] += A;
196  ctx->state[1] += B;
197  ctx->state[2] += C;
198  ctx->state[3] += D;
199 }
200 
201 /*
202  * MD5 process buffer
203  */
204 void md5_update( md5_context *ctx, const unsigned char *input, size_t ilen )
205 {
206  size_t fill;
207  uint32_t left;
208 
209  if( ilen <= 0 )
210  return;
211 
212  left = ctx->total[0] & 0x3F;
213  fill = 64 - left;
214 
215  ctx->total[0] += (uint32_t) ilen;
216  ctx->total[0] &= 0xFFFFFFFF;
217 
218  if( ctx->total[0] < (uint32_t) ilen )
219  ctx->total[1]++;
220 
221  if( left && ilen >= fill )
222  {
223  memcpy( (void *) (ctx->buffer + left),
224  (void *) input, fill );
225  md5_process( ctx, ctx->buffer );
226  input += fill;
227  ilen -= fill;
228  left = 0;
229  }
230 
231  while( ilen >= 64 )
232  {
233  md5_process( ctx, input );
234  input += 64;
235  ilen -= 64;
236  }
237 
238  if( ilen > 0 )
239  {
240  memcpy( (void *) (ctx->buffer + left),
241  (void *) input, ilen );
242  }
243 }
244 
245 static const unsigned char md5_padding[64] =
246 {
247  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
248  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
249  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
250  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
251 };
252 
253 /*
254  * MD5 final digest
255  */
256 void md5_finish( md5_context *ctx, unsigned char output[16] )
257 {
258  uint32_t last, padn;
259  uint32_t high, low;
260  unsigned char msglen[8];
261 
262  high = ( ctx->total[0] >> 29 )
263  | ( ctx->total[1] << 3 );
264  low = ( ctx->total[0] << 3 );
265 
266  PUT_UINT32_LE( low, msglen, 0 );
267  PUT_UINT32_LE( high, msglen, 4 );
268 
269  last = ctx->total[0] & 0x3F;
270  padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
271 
272  md5_update( ctx, (unsigned char *) md5_padding, padn );
273  md5_update( ctx, msglen, 8 );
274 
275  PUT_UINT32_LE( ctx->state[0], output, 0 );
276  PUT_UINT32_LE( ctx->state[1], output, 4 );
277  PUT_UINT32_LE( ctx->state[2], output, 8 );
278  PUT_UINT32_LE( ctx->state[3], output, 12 );
279 }
280 
281 /*
282  * output = MD5( input buffer )
283  */
284 void md5( const unsigned char *input, size_t ilen, unsigned char output[16] )
285 {
286  md5_context ctx;
287 
288  md5_starts( &ctx );
289  md5_update( &ctx, input, ilen );
290  md5_finish( &ctx, output );
291 
292  memset( &ctx, 0, sizeof( md5_context ) );
293 }
294 
295 #if defined(POLARSSL_FS_IO)
296 /*
297  * output = MD5( file contents )
298  */
299 int md5_file( const char *path, unsigned char output[16] )
300 {
301  FILE *f;
302  size_t n;
303  md5_context ctx;
304  unsigned char buf[1024];
305 
306  if( ( f = fopen( path, "rb" ) ) == NULL )
308 
309  md5_starts( &ctx );
310 
311  while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 )
312  md5_update( &ctx, buf, n );
313 
314  md5_finish( &ctx, output );
315 
316  memset( &ctx, 0, sizeof( md5_context ) );
317 
318  if( ferror( f ) != 0 )
319  {
320  fclose( f );
322  }
323 
324  fclose( f );
325  return( 0 );
326 }
327 #endif /* POLARSSL_FS_IO */
328 
329 /*
330  * MD5 HMAC context setup
331  */
332 void md5_hmac_starts( md5_context *ctx, const unsigned char *key, size_t keylen )
333 {
334  size_t i;
335  unsigned char sum[16];
336 
337  if( keylen > 64 )
338  {
339  md5( key, keylen, sum );
340  keylen = 16;
341  key = sum;
342  }
343 
344  memset( ctx->ipad, 0x36, 64 );
345  memset( ctx->opad, 0x5C, 64 );
346 
347  for( i = 0; i < keylen; i++ )
348  {
349  ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] );
350  ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] );
351  }
352 
353  md5_starts( ctx );
354  md5_update( ctx, ctx->ipad, 64 );
355 
356  memset( sum, 0, sizeof( sum ) );
357 }
358 
359 /*
360  * MD5 HMAC process buffer
361  */
362 void md5_hmac_update( md5_context *ctx, const unsigned char *input, size_t ilen )
363 {
364  md5_update( ctx, input, ilen );
365 }
366 
367 /*
368  * MD5 HMAC final digest
369  */
370 void md5_hmac_finish( md5_context *ctx, unsigned char output[16] )
371 {
372  unsigned char tmpbuf[16];
373 
374  md5_finish( ctx, tmpbuf );
375  md5_starts( ctx );
376  md5_update( ctx, ctx->opad, 64 );
377  md5_update( ctx, tmpbuf, 16 );
378  md5_finish( ctx, output );
379 
380  memset( tmpbuf, 0, sizeof( tmpbuf ) );
381 }
382 
383 /*
384  * MD5 HMAC context reset
385  */
386 void md5_hmac_reset( md5_context *ctx )
387 {
388  md5_starts( ctx );
389  md5_update( ctx, ctx->ipad, 64 );
390 }
391 
392 /*
393  * output = HMAC-MD5( hmac key, input buffer )
394  */
395 void md5_hmac( const unsigned char *key, size_t keylen,
396  const unsigned char *input, size_t ilen,
397  unsigned char output[16] )
398 {
399  md5_context ctx;
400 
401  md5_hmac_starts( &ctx, key, keylen );
402  md5_hmac_update( &ctx, input, ilen );
403  md5_hmac_finish( &ctx, output );
404 
405  memset( &ctx, 0, sizeof( md5_context ) );
406 }
407 
408 #if defined(POLARSSL_SELF_TEST)
409 /*
410  * RFC 1321 test vectors
411  */
412 static unsigned char md5_test_buf[7][81] =
413 {
414  { "" },
415  { "a" },
416  { "abc" },
417  { "message digest" },
418  { "abcdefghijklmnopqrstuvwxyz" },
419  { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" },
420  { "12345678901234567890123456789012345678901234567890123456789012" \
421  "345678901234567890" }
422 };
423 
424 static const int md5_test_buflen[7] =
425 {
426  0, 1, 3, 14, 26, 62, 80
427 };
428 
429 static const unsigned char md5_test_sum[7][16] =
430 {
431  { 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04,
432  0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E },
433  { 0x0C, 0xC1, 0x75, 0xB9, 0xC0, 0xF1, 0xB6, 0xA8,
434  0x31, 0xC3, 0x99, 0xE2, 0x69, 0x77, 0x26, 0x61 },
435  { 0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0,
436  0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72 },
437  { 0xF9, 0x6B, 0x69, 0x7D, 0x7C, 0xB7, 0x93, 0x8D,
438  0x52, 0x5A, 0x2F, 0x31, 0xAA, 0xF1, 0x61, 0xD0 },
439  { 0xC3, 0xFC, 0xD3, 0xD7, 0x61, 0x92, 0xE4, 0x00,
440  0x7D, 0xFB, 0x49, 0x6C, 0xCA, 0x67, 0xE1, 0x3B },
441  { 0xD1, 0x74, 0xAB, 0x98, 0xD2, 0x77, 0xD9, 0xF5,
442  0xA5, 0x61, 0x1C, 0x2C, 0x9F, 0x41, 0x9D, 0x9F },
443  { 0x57, 0xED, 0xF4, 0xA2, 0x2B, 0xE3, 0xC9, 0x55,
444  0xAC, 0x49, 0xDA, 0x2E, 0x21, 0x07, 0xB6, 0x7A }
445 };
446 
447 /*
448  * RFC 2202 test vectors
449  */
450 static unsigned char md5_hmac_test_key[7][26] =
451 {
452  { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" },
453  { "Jefe" },
454  { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" },
455  { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10"
456  "\x11\x12\x13\x14\x15\x16\x17\x18\x19" },
457  { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C" },
458  { "" }, /* 0xAA 80 times */
459  { "" }
460 };
461 
462 static const int md5_hmac_test_keylen[7] =
463 {
464  16, 4, 16, 25, 16, 80, 80
465 };
466 
467 static unsigned char md5_hmac_test_buf[7][74] =
468 {
469  { "Hi There" },
470  { "what do ya want for nothing?" },
471  { "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"
472  "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"
473  "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"
474  "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"
475  "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" },
476  { "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
477  "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
478  "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
479  "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
480  "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" },
481  { "Test With Truncation" },
482  { "Test Using Larger Than Block-Size Key - Hash Key First" },
483  { "Test Using Larger Than Block-Size Key and Larger"
484  " Than One Block-Size Data" }
485 };
486 
487 static const int md5_hmac_test_buflen[7] =
488 {
489  8, 28, 50, 50, 20, 54, 73
490 };
491 
492 static const unsigned char md5_hmac_test_sum[7][16] =
493 {
494  { 0x92, 0x94, 0x72, 0x7A, 0x36, 0x38, 0xBB, 0x1C,
495  0x13, 0xF4, 0x8E, 0xF8, 0x15, 0x8B, 0xFC, 0x9D },
496  { 0x75, 0x0C, 0x78, 0x3E, 0x6A, 0xB0, 0xB5, 0x03,
497  0xEA, 0xA8, 0x6E, 0x31, 0x0A, 0x5D, 0xB7, 0x38 },
498  { 0x56, 0xBE, 0x34, 0x52, 0x1D, 0x14, 0x4C, 0x88,
499  0xDB, 0xB8, 0xC7, 0x33, 0xF0, 0xE8, 0xB3, 0xF6 },
500  { 0x69, 0x7E, 0xAF, 0x0A, 0xCA, 0x3A, 0x3A, 0xEA,
501  0x3A, 0x75, 0x16, 0x47, 0x46, 0xFF, 0xAA, 0x79 },
502  { 0x56, 0x46, 0x1E, 0xF2, 0x34, 0x2E, 0xDC, 0x00,
503  0xF9, 0xBA, 0xB9, 0x95 },
504  { 0x6B, 0x1A, 0xB7, 0xFE, 0x4B, 0xD7, 0xBF, 0x8F,
505  0x0B, 0x62, 0xE6, 0xCE, 0x61, 0xB9, 0xD0, 0xCD },
506  { 0x6F, 0x63, 0x0F, 0xAD, 0x67, 0xCD, 0xA0, 0xEE,
507  0x1F, 0xB1, 0xF5, 0x62, 0xDB, 0x3A, 0xA5, 0x3E }
508 };
509 
510 /*
511  * Checkup routine
512  */
513 int md5_self_test( int verbose )
514 {
515  int i, buflen;
516  unsigned char buf[1024];
517  unsigned char md5sum[16];
518  md5_context ctx;
519 
520  for( i = 0; i < 7; i++ )
521  {
522  if( verbose != 0 )
523  printf( " MD5 test #%d: ", i + 1 );
524 
525  md5( md5_test_buf[i], md5_test_buflen[i], md5sum );
526 
527  if( memcmp( md5sum, md5_test_sum[i], 16 ) != 0 )
528  {
529  if( verbose != 0 )
530  printf( "failed\n" );
531 
532  return( 1 );
533  }
534 
535  if( verbose != 0 )
536  printf( "passed\n" );
537  }
538 
539  if( verbose != 0 )
540  printf( "\n" );
541 
542  for( i = 0; i < 7; i++ )
543  {
544  if( verbose != 0 )
545  printf( " HMAC-MD5 test #%d: ", i + 1 );
546 
547  if( i == 5 || i == 6 )
548  {
549  memset( buf, '\xAA', buflen = 80 );
550  md5_hmac_starts( &ctx, buf, buflen );
551  }
552  else
553  md5_hmac_starts( &ctx, md5_hmac_test_key[i],
554  md5_hmac_test_keylen[i] );
555 
556  md5_hmac_update( &ctx, md5_hmac_test_buf[i],
557  md5_hmac_test_buflen[i] );
558 
559  md5_hmac_finish( &ctx, md5sum );
560 
561  buflen = ( i == 4 ) ? 12 : 16;
562 
563  if( memcmp( md5sum, md5_hmac_test_sum[i], buflen ) != 0 )
564  {
565  if( verbose != 0 )
566  printf( "failed\n" );
567 
568  return( 1 );
569  }
570 
571  if( verbose != 0 )
572  printf( "passed\n" );
573  }
574 
575  if( verbose != 0 )
576  printf( "\n" );
577 
578  return( 0 );
579 }
580 
581 #endif
582 
583 #endif