xref: /openbsd-src/gnu/usr.bin/cvs/lib/md5.c (revision 319d553ec0baf61c34d879094d6cf10af468d74a)
11e72d8d2Sderaadt /*
21e72d8d2Sderaadt  * This code implements the MD5 message-digest algorithm.
31e72d8d2Sderaadt  * The algorithm is due to Ron Rivest.  This code was
41e72d8d2Sderaadt  * written by Colin Plumb in 1993, no copyright is claimed.
51e72d8d2Sderaadt  * This code is in the public domain; do with it what you wish.
61e72d8d2Sderaadt  *
71e72d8d2Sderaadt  * Equivalent code is available from RSA Data Security, Inc.
81e72d8d2Sderaadt  * This code has been tested against that, and is equivalent,
91e72d8d2Sderaadt  * except that you don't need to include two pages of legalese
101e72d8d2Sderaadt  * with every copy.
111e72d8d2Sderaadt  *
121e72d8d2Sderaadt  * To compute the message digest of a chunk of bytes, declare an
131e72d8d2Sderaadt  * MD5Context structure, pass it to MD5Init, call MD5Update as
141e72d8d2Sderaadt  * needed on buffers full of bytes, and then call MD5Final, which
151e72d8d2Sderaadt  * will fill a supplied 16-byte array with the digest.
161e72d8d2Sderaadt  */
171e72d8d2Sderaadt 
18780d15dfStholo /* This code was modified in 1997 by Jim Kingdon of Cyclic Software to
19780d15dfStholo    not require an integer type which is exactly 32 bits.  This work
20780d15dfStholo    draws on the changes for the same purpose by Tatu Ylonen
21780d15dfStholo    <ylo@cs.hut.fi> as part of SSH, but since I didn't actually use
22780d15dfStholo    that code, there is no copyright issue.  I hereby disclaim
23780d15dfStholo    copyright in any changes I have made; this code remains in the
24780d15dfStholo    public domain.  */
25780d15dfStholo 
26c71bc7e2Stholo /* Note regarding cvs_* namespace: this avoids potential conflicts
27c71bc7e2Stholo    with libraries such as some versions of Kerberos.  No particular
28c71bc7e2Stholo    need to worry about whether the system supplies an MD5 library, as
29c71bc7e2Stholo    this file is only about 3k of object code.  */
30c71bc7e2Stholo 
31780d15dfStholo #ifdef HAVE_CONFIG_H
321e72d8d2Sderaadt #include "config.h"
33780d15dfStholo #endif
341e72d8d2Sderaadt 
35c71bc7e2Stholo #include <string.h>	/* for memcpy() and memset() */
361e72d8d2Sderaadt 
371e72d8d2Sderaadt /* Add prototype support.  */
381e72d8d2Sderaadt #ifndef PROTO
391e72d8d2Sderaadt #if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
401e72d8d2Sderaadt #define PROTO(ARGS) ARGS
411e72d8d2Sderaadt #else
421e72d8d2Sderaadt #define PROTO(ARGS) ()
431e72d8d2Sderaadt #endif
441e72d8d2Sderaadt #endif
451e72d8d2Sderaadt 
461e72d8d2Sderaadt #include "md5.h"
471e72d8d2Sderaadt 
48780d15dfStholo /* Little-endian byte-swapping routines.  Note that these do not
49c71bc7e2Stholo    depend on the size of datatypes such as cvs_uint32, nor do they require
50780d15dfStholo    us to detect the endianness of the machine we are running on.  It
51780d15dfStholo    is possible they should be macros for speed, but I would be
52780d15dfStholo    surprised if they were a performance bottleneck for MD5.  */
531e72d8d2Sderaadt 
54c71bc7e2Stholo static cvs_uint32
getu32(addr)55780d15dfStholo getu32 (addr)
56780d15dfStholo      const unsigned char *addr;
571e72d8d2Sderaadt {
58780d15dfStholo 	return (((((unsigned long)addr[3] << 8) | addr[2]) << 8)
59780d15dfStholo 		| addr[1]) << 8 | addr[0];
601e72d8d2Sderaadt }
61780d15dfStholo 
62780d15dfStholo static void
putu32(data,addr)63780d15dfStholo putu32 (data, addr)
64c71bc7e2Stholo      cvs_uint32 data;
65780d15dfStholo      unsigned char *addr;
66780d15dfStholo {
67780d15dfStholo 	addr[0] = (unsigned char)data;
68780d15dfStholo 	addr[1] = (unsigned char)(data >> 8);
69780d15dfStholo 	addr[2] = (unsigned char)(data >> 16);
70780d15dfStholo 	addr[3] = (unsigned char)(data >> 24);
71780d15dfStholo }
721e72d8d2Sderaadt 
731e72d8d2Sderaadt /*
741e72d8d2Sderaadt  * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
751e72d8d2Sderaadt  * initialization constants.
761e72d8d2Sderaadt  */
771e72d8d2Sderaadt void
cvs_MD5Init(ctx)78c71bc7e2Stholo cvs_MD5Init (ctx)
79c71bc7e2Stholo      struct cvs_MD5Context *ctx;
801e72d8d2Sderaadt {
811e72d8d2Sderaadt 	ctx->buf[0] = 0x67452301;
821e72d8d2Sderaadt 	ctx->buf[1] = 0xefcdab89;
831e72d8d2Sderaadt 	ctx->buf[2] = 0x98badcfe;
841e72d8d2Sderaadt 	ctx->buf[3] = 0x10325476;
851e72d8d2Sderaadt 
861e72d8d2Sderaadt 	ctx->bits[0] = 0;
871e72d8d2Sderaadt 	ctx->bits[1] = 0;
881e72d8d2Sderaadt }
891e72d8d2Sderaadt 
901e72d8d2Sderaadt /*
911e72d8d2Sderaadt  * Update context to reflect the concatenation of another buffer full
921e72d8d2Sderaadt  * of bytes.
931e72d8d2Sderaadt  */
941e72d8d2Sderaadt void
cvs_MD5Update(ctx,buf,len)95c71bc7e2Stholo cvs_MD5Update (ctx, buf, len)
96c71bc7e2Stholo      struct cvs_MD5Context *ctx;
971e72d8d2Sderaadt      unsigned char const *buf;
981e72d8d2Sderaadt      unsigned len;
991e72d8d2Sderaadt {
100c71bc7e2Stholo 	cvs_uint32 t;
1011e72d8d2Sderaadt 
1021e72d8d2Sderaadt 	/* Update bitcount */
1031e72d8d2Sderaadt 
1041e72d8d2Sderaadt 	t = ctx->bits[0];
105c71bc7e2Stholo 	if ((ctx->bits[0] = (t + ((cvs_uint32)len << 3)) & 0xffffffff) < t)
1061e72d8d2Sderaadt 		ctx->bits[1]++;	/* Carry from low to high */
1071e72d8d2Sderaadt 	ctx->bits[1] += len >> 29;
1081e72d8d2Sderaadt 
1091e72d8d2Sderaadt 	t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
1101e72d8d2Sderaadt 
1111e72d8d2Sderaadt 	/* Handle any leading odd-sized chunks */
1121e72d8d2Sderaadt 
1131e72d8d2Sderaadt 	if ( t ) {
114780d15dfStholo 		unsigned char *p = ctx->in + t;
1151e72d8d2Sderaadt 
1161e72d8d2Sderaadt 		t = 64-t;
1171e72d8d2Sderaadt 		if (len < t) {
1181e72d8d2Sderaadt 			memcpy(p, buf, len);
1191e72d8d2Sderaadt 			return;
1201e72d8d2Sderaadt 		}
1211e72d8d2Sderaadt 		memcpy(p, buf, t);
122c71bc7e2Stholo 		cvs_MD5Transform (ctx->buf, ctx->in);
1231e72d8d2Sderaadt 		buf += t;
1241e72d8d2Sderaadt 		len -= t;
1251e72d8d2Sderaadt 	}
1261e72d8d2Sderaadt 
1271e72d8d2Sderaadt 	/* Process data in 64-byte chunks */
1281e72d8d2Sderaadt 
1291e72d8d2Sderaadt 	while (len >= 64) {
1301e72d8d2Sderaadt 		memcpy(ctx->in, buf, 64);
131c71bc7e2Stholo 		cvs_MD5Transform (ctx->buf, ctx->in);
1321e72d8d2Sderaadt 		buf += 64;
1331e72d8d2Sderaadt 		len -= 64;
1341e72d8d2Sderaadt 	}
1351e72d8d2Sderaadt 
1361e72d8d2Sderaadt 	/* Handle any remaining bytes of data. */
1371e72d8d2Sderaadt 
1381e72d8d2Sderaadt 	memcpy(ctx->in, buf, len);
1391e72d8d2Sderaadt }
1401e72d8d2Sderaadt 
1411e72d8d2Sderaadt /*
1421e72d8d2Sderaadt  * Final wrapup - pad to 64-byte boundary with the bit pattern
1431e72d8d2Sderaadt  * 1 0* (64-bit count of bits processed, MSB-first)
1441e72d8d2Sderaadt  */
1451e72d8d2Sderaadt void
cvs_MD5Final(digest,ctx)146c71bc7e2Stholo cvs_MD5Final (digest, ctx)
1471e72d8d2Sderaadt      unsigned char digest[16];
148c71bc7e2Stholo      struct cvs_MD5Context *ctx;
1491e72d8d2Sderaadt {
1501e72d8d2Sderaadt 	unsigned count;
1511e72d8d2Sderaadt 	unsigned char *p;
1521e72d8d2Sderaadt 
1531e72d8d2Sderaadt 	/* Compute number of bytes mod 64 */
1541e72d8d2Sderaadt 	count = (ctx->bits[0] >> 3) & 0x3F;
1551e72d8d2Sderaadt 
1561e72d8d2Sderaadt 	/* Set the first char of padding to 0x80.  This is safe since there is
1571e72d8d2Sderaadt 	   always at least one byte free */
1581e72d8d2Sderaadt 	p = ctx->in + count;
1591e72d8d2Sderaadt 	*p++ = 0x80;
1601e72d8d2Sderaadt 
1611e72d8d2Sderaadt 	/* Bytes of padding needed to make 64 bytes */
1621e72d8d2Sderaadt 	count = 64 - 1 - count;
1631e72d8d2Sderaadt 
1641e72d8d2Sderaadt 	/* Pad out to 56 mod 64 */
1651e72d8d2Sderaadt 	if (count < 8) {
1661e72d8d2Sderaadt 		/* Two lots of padding:  Pad the first block to 64 bytes */
1671e72d8d2Sderaadt 		memset(p, 0, count);
168c71bc7e2Stholo 		cvs_MD5Transform (ctx->buf, ctx->in);
1691e72d8d2Sderaadt 
1701e72d8d2Sderaadt 		/* Now fill the next block with 56 bytes */
1711e72d8d2Sderaadt 		memset(ctx->in, 0, 56);
1721e72d8d2Sderaadt 	} else {
1731e72d8d2Sderaadt 		/* Pad block to 56 bytes */
1741e72d8d2Sderaadt 		memset(p, 0, count-8);
1751e72d8d2Sderaadt 	}
1761e72d8d2Sderaadt 
1771e72d8d2Sderaadt 	/* Append length in bits and transform */
178780d15dfStholo 	putu32(ctx->bits[0], ctx->in + 56);
179780d15dfStholo 	putu32(ctx->bits[1], ctx->in + 60);
1801e72d8d2Sderaadt 
181c71bc7e2Stholo 	cvs_MD5Transform (ctx->buf, ctx->in);
182780d15dfStholo 	putu32(ctx->buf[0], digest);
183780d15dfStholo 	putu32(ctx->buf[1], digest + 4);
184780d15dfStholo 	putu32(ctx->buf[2], digest + 8);
185780d15dfStholo 	putu32(ctx->buf[3], digest + 12);
186*319d553eSotto 	memset(ctx, 0, sizeof(*ctx));	/* In case it's sensitive */
1871e72d8d2Sderaadt }
1881e72d8d2Sderaadt 
1891e72d8d2Sderaadt #ifndef ASM_MD5
1901e72d8d2Sderaadt 
1911e72d8d2Sderaadt /* The four core functions - F1 is optimized somewhat */
1921e72d8d2Sderaadt 
1931e72d8d2Sderaadt /* #define F1(x, y, z) (x & y | ~x & z) */
1941e72d8d2Sderaadt #define F1(x, y, z) (z ^ (x & (y ^ z)))
1951e72d8d2Sderaadt #define F2(x, y, z) F1(z, x, y)
1961e72d8d2Sderaadt #define F3(x, y, z) (x ^ y ^ z)
1971e72d8d2Sderaadt #define F4(x, y, z) (y ^ (x | ~z))
1981e72d8d2Sderaadt 
1991e72d8d2Sderaadt /* This is the central step in the MD5 algorithm. */
2001e72d8d2Sderaadt #define MD5STEP(f, w, x, y, z, data, s) \
201780d15dfStholo 	( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x )
2021e72d8d2Sderaadt 
2031e72d8d2Sderaadt /*
2041e72d8d2Sderaadt  * The core of the MD5 algorithm, this alters an existing MD5 hash to
2051e72d8d2Sderaadt  * reflect the addition of 16 longwords of new data.  MD5Update blocks
2061e72d8d2Sderaadt  * the data and converts bytes into longwords for this routine.
2071e72d8d2Sderaadt  */
2081e72d8d2Sderaadt void
cvs_MD5Transform(buf,inraw)209c71bc7e2Stholo cvs_MD5Transform (buf, inraw)
210c71bc7e2Stholo      cvs_uint32 buf[4];
211780d15dfStholo      const unsigned char inraw[64];
2121e72d8d2Sderaadt {
213c71bc7e2Stholo 	register cvs_uint32 a, b, c, d;
214c71bc7e2Stholo 	cvs_uint32 in[16];
215780d15dfStholo 	int i;
216780d15dfStholo 
217780d15dfStholo 	for (i = 0; i < 16; ++i)
218780d15dfStholo 		in[i] = getu32 (inraw + 4 * i);
2191e72d8d2Sderaadt 
2201e72d8d2Sderaadt 	a = buf[0];
2211e72d8d2Sderaadt 	b = buf[1];
2221e72d8d2Sderaadt 	c = buf[2];
2231e72d8d2Sderaadt 	d = buf[3];
2241e72d8d2Sderaadt 
2251e72d8d2Sderaadt 	MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478,  7);
2261e72d8d2Sderaadt 	MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
2271e72d8d2Sderaadt 	MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
2281e72d8d2Sderaadt 	MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
2291e72d8d2Sderaadt 	MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf,  7);
2301e72d8d2Sderaadt 	MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
2311e72d8d2Sderaadt 	MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
2321e72d8d2Sderaadt 	MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
2331e72d8d2Sderaadt 	MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8,  7);
2341e72d8d2Sderaadt 	MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
2351e72d8d2Sderaadt 	MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
2361e72d8d2Sderaadt 	MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
2371e72d8d2Sderaadt 	MD5STEP(F1, a, b, c, d, in[12]+0x6b901122,  7);
2381e72d8d2Sderaadt 	MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
2391e72d8d2Sderaadt 	MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
2401e72d8d2Sderaadt 	MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
2411e72d8d2Sderaadt 
2421e72d8d2Sderaadt 	MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562,  5);
2431e72d8d2Sderaadt 	MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340,  9);
2441e72d8d2Sderaadt 	MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
2451e72d8d2Sderaadt 	MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
2461e72d8d2Sderaadt 	MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d,  5);
2471e72d8d2Sderaadt 	MD5STEP(F2, d, a, b, c, in[10]+0x02441453,  9);
2481e72d8d2Sderaadt 	MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
2491e72d8d2Sderaadt 	MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
2501e72d8d2Sderaadt 	MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6,  5);
2511e72d8d2Sderaadt 	MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6,  9);
2521e72d8d2Sderaadt 	MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
2531e72d8d2Sderaadt 	MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
2541e72d8d2Sderaadt 	MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905,  5);
2551e72d8d2Sderaadt 	MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8,  9);
2561e72d8d2Sderaadt 	MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
2571e72d8d2Sderaadt 	MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
2581e72d8d2Sderaadt 
2591e72d8d2Sderaadt 	MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942,  4);
2601e72d8d2Sderaadt 	MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
2611e72d8d2Sderaadt 	MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
2621e72d8d2Sderaadt 	MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
2631e72d8d2Sderaadt 	MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44,  4);
2641e72d8d2Sderaadt 	MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
2651e72d8d2Sderaadt 	MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
2661e72d8d2Sderaadt 	MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
2671e72d8d2Sderaadt 	MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6,  4);
2681e72d8d2Sderaadt 	MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
2691e72d8d2Sderaadt 	MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
2701e72d8d2Sderaadt 	MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
2711e72d8d2Sderaadt 	MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039,  4);
2721e72d8d2Sderaadt 	MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
2731e72d8d2Sderaadt 	MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
2741e72d8d2Sderaadt 	MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
2751e72d8d2Sderaadt 
2761e72d8d2Sderaadt 	MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244,  6);
2771e72d8d2Sderaadt 	MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
2781e72d8d2Sderaadt 	MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
2791e72d8d2Sderaadt 	MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
2801e72d8d2Sderaadt 	MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3,  6);
2811e72d8d2Sderaadt 	MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
2821e72d8d2Sderaadt 	MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
2831e72d8d2Sderaadt 	MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
2841e72d8d2Sderaadt 	MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f,  6);
2851e72d8d2Sderaadt 	MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
2861e72d8d2Sderaadt 	MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
2871e72d8d2Sderaadt 	MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
2881e72d8d2Sderaadt 	MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82,  6);
2891e72d8d2Sderaadt 	MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
2901e72d8d2Sderaadt 	MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
2911e72d8d2Sderaadt 	MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
2921e72d8d2Sderaadt 
2931e72d8d2Sderaadt 	buf[0] += a;
2941e72d8d2Sderaadt 	buf[1] += b;
2951e72d8d2Sderaadt 	buf[2] += c;
2961e72d8d2Sderaadt 	buf[3] += d;
2971e72d8d2Sderaadt }
2981e72d8d2Sderaadt #endif
299780d15dfStholo 
300780d15dfStholo #ifdef TEST
301780d15dfStholo /* Simple test program.  Can use it to manually run the tests from
302780d15dfStholo    RFC1321 for example.  */
303780d15dfStholo #include <stdio.h>
304780d15dfStholo 
305780d15dfStholo int
main(int argc,char ** argv)306780d15dfStholo main (int argc, char **argv)
307780d15dfStholo {
308c71bc7e2Stholo 	struct cvs_MD5Context context;
309780d15dfStholo 	unsigned char checksum[16];
310780d15dfStholo 	int i;
311780d15dfStholo 	int j;
312780d15dfStholo 
313780d15dfStholo 	if (argc < 2)
314780d15dfStholo 	{
315780d15dfStholo 		fprintf (stderr, "usage: %s string-to-hash\n", argv[0]);
316780d15dfStholo 		exit (1);
317780d15dfStholo 	}
318780d15dfStholo 	for (j = 1; j < argc; ++j)
319780d15dfStholo 	{
320780d15dfStholo 		printf ("MD5 (\"%s\") = ", argv[j]);
321c71bc7e2Stholo 		cvs_MD5Init (&context);
322c71bc7e2Stholo 		cvs_MD5Update (&context, argv[j], strlen (argv[j]));
323c71bc7e2Stholo 		cvs_MD5Final (checksum, &context);
324780d15dfStholo 		for (i = 0; i < 16; i++)
325780d15dfStholo 		{
326780d15dfStholo 			printf ("%02x", (unsigned int) checksum[i]);
327780d15dfStholo 		}
328780d15dfStholo 		printf ("\n");
329780d15dfStholo 	}
330780d15dfStholo 	return 0;
331780d15dfStholo }
332780d15dfStholo #endif /* TEST */
333