/*---------------------------------------------------------------------------*/
/* hash.cpp                                                                  */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include <platform.h>
#include "../ilib/hash.h"

#ifndef __P
#define __P(args)			args
#endif

#ifndef __BIT_TYPES_DEFINED__
typedef signed char         int8_t;
typedef	unsigned char		u_int8_t;
typedef	unsigned char		uint8_t;
typedef	short				int16_t;
typedef	unsigned short		u_int16_t;
typedef	unsigned short		uint16_t;
typedef	int					int32_t;
typedef	unsigned int		u_int32_t;
typedef	unsigned int		uint32_t;
typedef unsigned long long  ulong64;
typedef unsigned long long  uint64_t;
#endif

namespace _rsa_md4
{
    /* MD4 */
    /*	$NetBSD: md4c.c,v 1.5 2012/03/20 16:21:41 matt Exp $	*/

    /*
     * This file is derived from the RSA Data Security, Inc. MD4 Message-Digest
     * Algorithm and has been modified by Jason R. Thorpe <thorpej@NetBSD.org>
     * for portability and formatting.
     */

     /*
      * Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
      *
      * License to copy and use this software is granted provided that it
      * is identified as the "RSA Data Security, Inc. MD4 Message-Digest
      * Algorithm" in all material mentioning or referencing this software
      * or this function.
      *
      * License is also granted to make and use derivative works provided
      * that such works are identified as "derived from the RSA Data
      * Security, Inc. MD4 Message-Digest Algorithm" in all material
      * mentioning or referencing the derived work.
      *
      * RSA Data Security, Inc. makes no representations concerning either
      * the merchantability of this software or the suitability of this
      * software for any particular purpose. It is provided "as is"
      * without express or implied warranty of any kind.
      *
      * These notices must be retained in any copies of any part of this
      * documentation and/or software.
      */
    
    typedef struct MD4Context {
        uint32_t state[4];
        uint32_t count[2];
        unsigned char buffer[64];
    } MD4_CTX;

    /* md4c.c */

    typedef unsigned char * POINTER;
    typedef uint16_t UINT2;
    typedef uint32_t UINT4;

    /*
     * Constants for MD4Transform routine.
     */
#define S11 3
#define S12 7
#define S13 11
#define S14 19
#define S21 3
#define S22 5
#define S23 9
#define S24 13
#define S31 3
#define S32 9
#define S33 11
#define S34 15

    static void MD4Transform __P((UINT4[4], const unsigned char[64]));

    static void Encode __P((unsigned char *, UINT4 *, unsigned int));
    static void Decode __P((UINT4 *, const unsigned char *, unsigned int));

    static const unsigned char PADDING[64] = {
        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };

    /*
 * F, G and H are basic MD4 functions.
 */
#define F(x, y, z)	(((x) & (y)) | ((~x) & (z)))
#define G(x, y, z)	(((x) & (y)) | ((x) & (z)) | ((y) & (z)))
#define H(x, y, z)	((x) ^ (y) ^ (z))

 /*
  * ROTATE_LEFT rotates x left n bits.
  */
#define ROTATE_LEFT(x, n)	(((x) << (n)) | ((x) >> (32-(n))))

  /*
   * FF, GG and HH are transformations for rounds 1, 2 and 3.
   * Rotation is separate from addition to prevent recomputation.
   */
#define FF(a, b, c, d, x, s) { \
	(a) += F ((b), (c), (d)) + (x); \
	(a) = ROTATE_LEFT ((a), (s)); \
}

#define GG(a, b, c, d, x, s) { \
	(a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \
	(a) = ROTATE_LEFT ((a), (s)); \
}

#define HH(a, b, c, d, x, s) { \
	(a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \
	(a) = ROTATE_LEFT ((a), (s)); \
}

    /*
     * MD4 initialization. Begins an MD4 operation, writing a new context.
     */
    void
    MD4Init(MD4_CTX *context)		/* context */
    {

	    //_DIAGASSERT(context != 0);

	    context->count[0] = context->count[1] = 0;

	    /* Load magic initialization constants. */
	    context->state[0] = 0x67452301;
	    context->state[1] = 0xefcdab89;
	    context->state[2] = 0x98badcfe;
	    context->state[3] = 0x10325476;
    }

    /*
     * MD4 block update operation.  Continues an MD4 message-digest
     * operation, processing another message block, and updating the
     * context.
     */
    void
    MD4Update (MD4_CTX *context,		/* context */
	    const unsigned char *input,	/* input block */
	    unsigned int inputLen)		/* length of input block */
    {
	    unsigned int i, idx, partLen;

	    //_DIAGASSERT(context != 0);
	    //_DIAGASSERT(input != 0);

	    /* Compute number of bytes mod 64 */
	    idx = (unsigned int)((context->count[0] >> 3) & 0x3F);

	    /* Update number of bits */
	    if ((context->count[0] += ((UINT4)inputLen << 3))
	        < ((UINT4)inputLen << 3))
		    context->count[1]++;
	    context->count[1] += ((UINT4)inputLen >> 29);

	    partLen = 64 - idx;

	    /* Transform as many times as possible. */
	    if (inputLen >= partLen) {
		    memcpy((POINTER)&context->buffer[idx], (POINTER)input, partLen);
		    MD4Transform(context->state, context->buffer);

		    for (i = partLen; i + 63 < inputLen; i += 64)
			    MD4Transform(context->state, &input[i]);

		    idx = 0;
	    } else
		    i = 0;

	    /* Buffer remaining input */
	    memcpy(&context->buffer[idx], (POINTER)&input[i], inputLen - i);
    }

    /*
     * MD4 finalization.  Ends an MD4 message-digest operation, writing the
     * message digest and zeroing the context.
     */
    void
    MD4Final (unsigned char digest[16],	/* message digest */
	    MD4_CTX *context)		/* context */
    {
	    unsigned char bits[8];
	    unsigned int idx, padLen;

	    //_DIAGASSERT(digest != 0);
	    //_DIAGASSERT(context != 0);

	    /* Save number of bits */
	    Encode(bits, context->count, 8);

	    /* Pad out to 56 mod 64. */
	    idx = (unsigned int)((context->count[0] >> 3) & 0x3f);
	    padLen = (idx < 56) ? (56 - idx) : (120 - idx);
	    MD4Update(context, PADDING, padLen);

	    /* Append length (before padding) */
	    MD4Update(context, bits, 8);

	    /* Store state in digest */
	    Encode(digest, context->state, 16);

	    /* Zeroize sensitive information. */
	    memset((POINTER)context, 0, sizeof(*context));
    }

    /*
     * MD4 basic transformation.  Transforms state based on block.
     */
    static void
    MD4Transform (UINT4 state[4], const unsigned char block[64])
    {
	    UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];

	    Decode(x, block, 64);

	    /* Round 1 */
	    FF (a, b, c, d, x[ 0], S11); /* 1 */
	    FF (d, a, b, c, x[ 1], S12); /* 2 */
	    FF (c, d, a, b, x[ 2], S13); /* 3 */
	    FF (b, c, d, a, x[ 3], S14); /* 4 */
	    FF (a, b, c, d, x[ 4], S11); /* 5 */
	    FF (d, a, b, c, x[ 5], S12); /* 6 */
	    FF (c, d, a, b, x[ 6], S13); /* 7 */
	    FF (b, c, d, a, x[ 7], S14); /* 8 */
	    FF (a, b, c, d, x[ 8], S11); /* 9 */
	    FF (d, a, b, c, x[ 9], S12); /* 10 */
	    FF (c, d, a, b, x[10], S13); /* 11 */
	    FF (b, c, d, a, x[11], S14); /* 12 */
	    FF (a, b, c, d, x[12], S11); /* 13 */
	    FF (d, a, b, c, x[13], S12); /* 14 */
	    FF (c, d, a, b, x[14], S13); /* 15 */
	    FF (b, c, d, a, x[15], S14); /* 16 */

	    /* Round 2 */
	    GG (a, b, c, d, x[ 0], S21); /* 17 */
	    GG (d, a, b, c, x[ 4], S22); /* 18 */
	    GG (c, d, a, b, x[ 8], S23); /* 19 */
	    GG (b, c, d, a, x[12], S24); /* 20 */
	    GG (a, b, c, d, x[ 1], S21); /* 21 */
	    GG (d, a, b, c, x[ 5], S22); /* 22 */
	    GG (c, d, a, b, x[ 9], S23); /* 23 */
	    GG (b, c, d, a, x[13], S24); /* 24 */
	    GG (a, b, c, d, x[ 2], S21); /* 25 */
	    GG (d, a, b, c, x[ 6], S22); /* 26 */
	    GG (c, d, a, b, x[10], S23); /* 27 */
	    GG (b, c, d, a, x[14], S24); /* 28 */
	    GG (a, b, c, d, x[ 3], S21); /* 29 */
	    GG (d, a, b, c, x[ 7], S22); /* 30 */
	    GG (c, d, a, b, x[11], S23); /* 31 */
	    GG (b, c, d, a, x[15], S24); /* 32 */

	    /* Round 3 */
	    HH (a, b, c, d, x[ 0], S31); /* 33 */
	    HH (d, a, b, c, x[ 8], S32); /* 34 */
	    HH (c, d, a, b, x[ 4], S33); /* 35 */
	    HH (b, c, d, a, x[12], S34); /* 36 */
	    HH (a, b, c, d, x[ 2], S31); /* 37 */
	    HH (d, a, b, c, x[10], S32); /* 38 */
	    HH (c, d, a, b, x[ 6], S33); /* 39 */
	    HH (b, c, d, a, x[14], S34); /* 40 */
	    HH (a, b, c, d, x[ 1], S31); /* 41 */
	    HH (d, a, b, c, x[ 9], S32); /* 42 */
	    HH (c, d, a, b, x[ 5], S33); /* 43 */
	    HH (b, c, d, a, x[13], S34); /* 44 */
	    HH (a, b, c, d, x[ 3], S31); /* 45 */
	    HH (d, a, b, c, x[11], S32); /* 46 */
	    HH (c, d, a, b, x[ 7], S33); /* 47 */
	    HH (b, c, d, a, x[15], S34); /* 48 */

	    state[0] += a;
	    state[1] += b;
	    state[2] += c;
	    state[3] += d;

	    /* Zeroize sensitive information. */
	    memset((POINTER)x, 0, sizeof (x));
    }

    /*
     * Encodes input (UINT4) into output (unsigned char). Assumes len is
     * a multiple of 4.
     */
    static void
    Encode(unsigned char *output, UINT4 *input, unsigned int len)
    {
	    unsigned int i, j;

	    for (i = 0, j = 0; j < len; i++, j += 4) {
		    output[j] = (unsigned char)(input[i] & 0xff);
		    output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
		    output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
		    output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
	    }
    }

    /*
     * Decodes input (unsigned char) into output (UINT4). Assumes len is
     * a multiple of 4.
     */
    static void
    Decode(UINT4 *output, const unsigned char *input, unsigned int len)
    {
	    unsigned int i, j;

	    for (i = 0, j = 0; j < len; i++, j += 4)
		    output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
		        (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
    }

    // cleanup definitions
#undef S11
#undef S12
#undef S13
#undef S14
#undef S21
#undef S22
#undef S23
#undef S24
#undef S31
#undef S32
#undef S33
#undef S34
#undef F
#undef G
#undef H
#undef FF
#undef GG
#undef HH
#undef ROTATE_LEFT
}

namespace _rsa_md5 {
    /* MD5 */
    /*	$NetBSD: md5c.c,v 1.5 2012/03/20 16:21:41 matt Exp $	*/

    /*
     * This file is derived from the RSA Data Security, Inc. MD5 Message-Digest
     * Algorithm and has been modified by Jason R. Thorpe <thorpej@NetBSD.org>
     * for portability and formatting.
     */

     /*
      * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
      * rights reserved.
      *
      * License to copy and use this software is granted provided that it
      * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
      * Algorithm" in all material mentioning or referencing this software
      * or this function.
      *
      * License is also granted to make and use derivative works provided
      * that such works are identified as "derived from the RSA Data
      * Security, Inc. MD5 Message-Digest Algorithm" in all material
      * mentioning or referencing the derived work.
      *
      * RSA Data Security, Inc. makes no representations concerning either
      * the merchantability of this software or the suitability of this
      * software for any particular purpose. It is provided "as is"
      * without express or implied warranty of any kind.
      *
      * These notices must be retained in any copies of any part of this
      * documentation and/or software.
      */

    typedef struct MD5Context {
        u_int32_t state[4];
        u_int32_t count[2];
        unsigned char buffer[64];
    } MD5_CTX;

    /* md5c.c */

#define	ZEROIZE(d, l)		memset((d), 0, (l))

    typedef unsigned char * POINTER;
    typedef u_int16_t UINT2;
    typedef u_int32_t UINT4;

    /*
     * Constants for MD5Transform routine.
     */
#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21

    static void MD5Transform __P((UINT4 [4], const unsigned char [64]));

    static void Encode __P((unsigned char *, UINT4 *, unsigned int));
    static void Decode __P((UINT4 *, const unsigned char *, unsigned int));

    /*
     * Encodes input (UINT4) into output (unsigned char).  Assumes len is
     * a multiple of 4.
     */
    static void
    Encode (unsigned char *output,
	    UINT4 *input,
	    unsigned int len)
    {
	    unsigned int i, j;

	    for (i = 0, j = 0; j < len; i++, j += 4) {
		    output[j] = (unsigned char)(input[i] & 0xff);
		    output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
		    output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
		    output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
	    }
    }

    /*
     * Decodes input (unsigned char) into output (UINT4).  Assumes len is
     * a multiple of 4.
     */
    static void
    Decode (UINT4 *output,
	    const unsigned char *input,
	    unsigned int len)
    {
	    unsigned int i, j;

	    for (i = 0, j = 0; j < len; i++, j += 4)
		    output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
		        (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
    }

    static const unsigned char PADDING[64] = {
	    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };

    /*
     * F, G, H and I are basic MD5 functions.
     */
#define F(x, y, z)	(((x) & (y)) | ((~x) & (z)))
#define G(x, y, z)	(((x) & (z)) | ((y) & (~z)))
#define H(x, y, z)	((x) ^ (y) ^ (z))
#define I(x, y, z)	((y) ^ ((x) | (~z)))

    /*
     * ROTATE_LEFT rotates x left n bits.
     */
#define ROTATE_LEFT(x, n)	(((x) << (n)) | ((x) >> (32-(n))))

    /*
     * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
     * Rotation is separate from addition to prevent recomputation.
     */
#define FF(a, b, c, d, x, s, ac) { \
	(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
	(a) = ROTATE_LEFT ((a), (s)); \
	(a) += (b); \
}

#define GG(a, b, c, d, x, s, ac) { \
	(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
	(a) = ROTATE_LEFT ((a), (s)); \
	(a) += (b); \
}

#define HH(a, b, c, d, x, s, ac) { \
	(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
	(a) = ROTATE_LEFT ((a), (s)); \
	(a) += (b); \
}

#define II(a, b, c, d, x, s, ac) { \
	(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
	(a) = ROTATE_LEFT ((a), (s)); \
	(a) += (b); \
}

    /*
     * MD5 initialization. Begins an MD5 operation, writing a new context.
     */
    void
    MD5Init(MD5_CTX *context)
    {
	    context->count[0] = context->count[1] = 0;

	    /* Load magic initialization constants. */
	    context->state[0] = 0x67452301;
	    context->state[1] = 0xefcdab89;
	    context->state[2] = 0x98badcfe;
	    context->state[3] = 0x10325476;
    }

    /*
     * MD5 block update operation.  Continues an MD5 message-digest
     * operation, processing another message block, and updating the
     * context.
     */
    void
    MD5Update(MD5_CTX *context,
	    const unsigned char *input,	/* input block */
	    unsigned int inputLen)		/* length of input block */
    {
	    unsigned int i, idx, partLen;

	    /* Compute number of bytes mod 64 */
	    idx = (unsigned int)((context->count[0] >> 3) & 0x3F);

	    /* Update number of bits */
	    if ((context->count[0] += ((UINT4)inputLen << 3))
	        < ((UINT4)inputLen << 3))
		    context->count[1]++;
	    context->count[1] += ((UINT4)inputLen >> 29);

	    partLen = 64 - idx;

	    /* Transform as many times as possible. */
	    if (inputLen >= partLen) {
		    memcpy((POINTER)&context->buffer[idx], (POINTER)input, partLen);
		    MD5Transform(context->state, context->buffer);

		    for (i = partLen; i + 63 < inputLen; i += 64)
			    MD5Transform(context->state, &input[i]);

		    idx = 0;
	    } else
		    i = 0;

	    /* Buffer remaining input */
	    memcpy((POINTER)&context->buffer[idx], (POINTER)&input[i], inputLen - i);
    }

    /*
     * MD5 finalization.  Ends an MD5 message-digest operation, writing the
     * message digest and zeroing the context.
     */
    void
    MD5Final(unsigned char digest[16],	/* message digest */
	    MD5_CTX *context)		/* context */
    {
	    unsigned char bits[8];
	    unsigned int idx, padLen;

	    /* Save number of bits */
	    Encode(bits, context->count, 8);

	    /* Pad out to 56 mod 64. */
	    idx = (unsigned int)((context->count[0] >> 3) & 0x3f);
	    padLen = (idx < 56) ? (56 - idx) : (120 - idx);
	    MD5Update (context, PADDING, padLen);

	    /* Append length (before padding) */
	    MD5Update(context, bits, 8);

	    /* Store state in digest */
	    Encode(digest, context->state, 16);

	    /* Zeroize sensitive information. */
	    ZEROIZE((POINTER)(void *)context, sizeof(*context));
    }

    /*
     * MD5 basic transformation. Transforms state based on block.
     */
    static void
    MD5Transform(UINT4 state[4], const unsigned char block[64])
    {
	    UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];

	    Decode(x, block, 64);

	    /* Round 1 */
	    FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
	    FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
	    FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
	    FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
	    FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
	    FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
	    FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
	    FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
	    FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
	    FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
	    FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
	    FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
	    FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
	    FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
	    FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
	    FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */

	    /* Round 2 */
	    GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
	    GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
	    GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
	    GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
	    GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
	    GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
	    GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
	    GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
	    GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
	    GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
	    GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
	    GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
	    GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
	    GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
	    GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
	    GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */

	    /* Round 3 */
	    HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
	    HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
	    HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
	    HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
	    HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
	    HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
	    HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
	    HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
	    HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
	    HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
	    HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
	    HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
	    HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
	    HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
	    HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
	    HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */

	    /* Round 4 */
	    II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
	    II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
	    II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
	    II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
	    II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
	    II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
	    II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
	    II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
	    II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
	    II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
	    II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
	    II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
	    II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
	    II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
	    II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
	    II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */

	    state[0] += a;
	    state[1] += b;
	    state[2] += c;
	    state[3] += d;

	    /* Zeroize sensitive information. */
	    ZEROIZE((POINTER)(void *)x, sizeof (x));
    }

    // cleanup defines
#undef	ZEROIZE
#undef S11
#undef S12
#undef S13
#undef S14
#undef S21
#undef S22
#undef S23
#undef S24
#undef S31
#undef S32
#undef S33
#undef S34
#undef S41
#undef S42
#undef S43
#undef S44
#undef F
#undef G
#undef H
#undef I
#undef ROTATE_LEFT
#undef FF
#undef GG
#undef HH
#undef II
}

namespace _openssl
{
    /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
    * All rights reserved.
    *
    * This package is an SSL implementation written
    * by Eric Young (eay@cryptsoft.com).
    * The implementation was written so as to conform with Netscapes SSL.
    *
    * This library is free for commercial and non-commercial use as long as
    * the following conditions are aheared to.  The following conditions
    * apply to all code found in this distribution, be it the RC4, RSA,
    * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
    * included with this distribution is covered by the same copyright terms
    * except that the holder is Tim Hudson (tjh@cryptsoft.com).
    *
    * Copyright remains Eric Young's, and as such any Copyright notices in
    * the code are not to be removed.
    * If this package is used in a product, Eric Young should be given attribution
    * as the author of the parts of the library used.
    * This can be in the form of a textual message at program startup or
    * in documentation (online or textual) provided with the package.
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions
    * are met:
    * 1. Redistributions of source code must retain the copyright
    *    notice, this list of conditions and the following disclaimer.
    * 2. Redistributions in binary form must reproduce the above copyright
    *    notice, this list of conditions and the following disclaimer in the
    *    documentation and/or other materials provided with the distribution.
    * 3. All advertising materials mentioning features or use of this software
    *    must display the following acknowledgement:
    *    "This product includes cryptographic software written by
    *     Eric Young (eay@cryptsoft.com)"
    *    The word 'cryptographic' can be left out if the rouines from the library
    *    being used are not cryptographic related :-).
    * 4. If you include any Windows specific code (or a derivative thereof) from
    *    the apps directory (application code) you must include an acknowledgement:
    *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
    *
    * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
    * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
    * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    * SUCH DAMAGE.
    *
    * The licence and distribution terms for any publically available version or
    * derivative of this code cannot be changed.  i.e. this code cannot simply be
    * copied and put under another distribution licence
    * [including the GNU Public Licence.]
    */

    /*
    * The code has been adapted by innovaphone
    */

#define U64(C)     C##ULL
#define SHA_LONG dword
#define SHA_LONG_LOG2 2
#define SHA_LONG64 ulong64
#define SHA_LBLOCK 16
#define SHA_CBLOCK (SHA_LBLOCK*4)
#define SHA_LAST_BLOCK (SHA_CBLOCK-8)
#define SHA_DIGEST_LENGTH 20
#define SHA256_CBLOCK (SHA_LBLOCK*4)
#define SHA224_DIGEST_LENGTH 28
#define SHA256_DIGEST_LENGTH 32
#define SHA384_DIGEST_LENGTH 48
#define SHA512_DIGEST_LENGTH 64
#define SHA512_CBLOCK (SHA_LBLOCK*8)
#define ROTATE(a,n)     (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))
#define HOST_c2l(c,l)	l =(((unsigned long)(*((c)++)))<<24);		\
    l|=(((unsigned long)(*((c)++)))<<16);		\
    l|=(((unsigned long)(*((c)++)))<< 8);		\
    l|=(((unsigned long)(*((c)++)))    );
#define HOST_l2c(l,c)	*((c)++)=(unsigned char)(((l)>>24)&0xff);	\
    *((c)++)=(unsigned char)(((l)>>16)&0xff);	\
    *((c)++)=(unsigned char)(((l)>> 8)&0xff);	\
    *((c)++)=(unsigned char)(((l)    )&0xff);
#define MD32_REG_T int

    typedef struct SHAstate_st
    {
        SHA_LONG h0, h1, h2, h3, h4;
        SHA_LONG Nl, Nh;
        SHA_LONG data[SHA_LBLOCK];
        unsigned int num;
    } SHA_CTX;

    typedef struct SHA256state_st
    {
        SHA_LONG h[8];
        SHA_LONG Nl, Nh;
        SHA_LONG data[SHA_LBLOCK];
        unsigned int num, md_len;
    } SHA256_CTX;

    typedef struct SHA512state_st
    {
        SHA_LONG64 h[8];
        SHA_LONG64 Nl, Nh;
        union {
            SHA_LONG64 d[SHA_LBLOCK];
            unsigned char p[SHA512_CBLOCK];
        } u;
        unsigned int num, md_len;
    } SHA512_CTX;

    /* SHA1 */
#define SHA_MAKE_STRING(c,s)   do {	\
    unsigned long ll;		\
    ll=(c)->h0; HOST_l2c(ll,(s));	\
    ll=(c)->h1; HOST_l2c(ll,(s));	\
    ll=(c)->h2; HOST_l2c(ll,(s));	\
    ll=(c)->h3; HOST_l2c(ll,(s));	\
    ll=(c)->h4; HOST_l2c(ll,(s));	\
    } while (0)
#define Xupdate(a,ix,ia,ib,ic,id)	( (a)=(ia^ib^ic^id),	\
    ix=(a)=ROTATE((a),1)	\
    )

#define K_00_19	0x5a827999UL
#define K_20_39 0x6ed9eba1UL
#define K_40_59 0x8f1bbcdcUL
#define K_60_79 0xca62c1d6UL
#define	F_00_19(b,c,d)	((((c) ^ (d)) & (b)) ^ (d)) 
#define	F_20_39(b,c,d)	((b) ^ (c) ^ (d))
#define F_40_59(b,c,d)	(((b) & (c)) | (((b)|(c)) & (d))) 
#define	F_60_79(b,c,d)	F_20_39(b,c,d)
#define BODY_00_15(i,a,b,c,d,e,f,xi) \
    (f)=xi+(e)+K_00_19+ROTATE((a),5)+F_00_19((b),(c),(d)); \
    (b)=ROTATE((b),30);
#define BODY_16_19(i,a,b,c,d,e,f,xi,xa,xb,xc,xd) \
    Xupdate(f,xi,xa,xb,xc,xd); \
    (f)+=(e)+K_00_19+ROTATE((a),5)+F_00_19((b),(c),(d)); \
    (b)=ROTATE((b),30);
#define BODY_20_31(i,a,b,c,d,e,f,xi,xa,xb,xc,xd) \
    Xupdate(f,xi,xa,xb,xc,xd); \
    (f)+=(e)+K_20_39+ROTATE((a),5)+F_20_39((b),(c),(d)); \
    (b)=ROTATE((b),30);
#define BODY_32_39(i,a,b,c,d,e,f,xa,xb,xc,xd) \
    Xupdate(f,xa,xa,xb,xc,xd); \
    (f)+=(e)+K_20_39+ROTATE((a),5)+F_20_39((b),(c),(d)); \
    (b)=ROTATE((b),30);
#define BODY_40_59(i,a,b,c,d,e,f,xa,xb,xc,xd) \
    Xupdate(f,xa,xa,xb,xc,xd); \
    (f)+=(e)+K_40_59+ROTATE((a),5)+F_40_59((b),(c),(d)); \
    (b)=ROTATE((b),30);
#define BODY_60_79(i,a,b,c,d,e,f,xa,xb,xc,xd) \
    Xupdate(f,xa,xa,xb,xc,xd); \
    (f)=xa+(e)+K_60_79+ROTATE((a),5)+F_60_79((b),(c),(d)); \
    (b)=ROTATE((b),30);
#define X(i)	XX##i
#if defined ARMV8_CRYPT
    extern "C" void armv8_sha1_block_data_order(SHA_CTX * c, const void* p, size_t num);
#endif
    static void sha1_block_data_order(SHA_CTX *c, const void *p, size_t num)
    {
#if defined ARMV8_CRYPT
        armv8_sha1_block_data_order(c, p, num);
#else
        const unsigned char *data = (const unsigned char *)p;
        unsigned MD32_REG_T A, B, C, D, E, T, l;
        unsigned MD32_REG_T	XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7,
            XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15;

        A = c->h0;
        B = c->h1;
        C = c->h2;
        D = c->h3;
        E = c->h4;

        for (;;)
        {
            const union { long one; char little; } is_endian = { 1 };

            if (!is_endian.little && sizeof(SHA_LONG) == 4 && ((size_t)p % 4) == 0)
            {
                const SHA_LONG *W = (const SHA_LONG *)data;

                X(0) = W[0];				X(1) = W[1];
                BODY_00_15(0, A, B, C, D, E, T, X(0));	X(2) = W[2];
                BODY_00_15(1, T, A, B, C, D, E, X(1));	X(3) = W[3];
                BODY_00_15(2, E, T, A, B, C, D, X(2));	X(4) = W[4];
                BODY_00_15(3, D, E, T, A, B, C, X(3));	X(5) = W[5];
                BODY_00_15(4, C, D, E, T, A, B, X(4));	X(6) = W[6];
                BODY_00_15(5, B, C, D, E, T, A, X(5));	X(7) = W[7];
                BODY_00_15(6, A, B, C, D, E, T, X(6));	X(8) = W[8];
                BODY_00_15(7, T, A, B, C, D, E, X(7));	X(9) = W[9];
                BODY_00_15(8, E, T, A, B, C, D, X(8));	X(10) = W[10];
                BODY_00_15(9, D, E, T, A, B, C, X(9));	X(11) = W[11];
                BODY_00_15(10, C, D, E, T, A, B, X(10));	X(12) = W[12];
                BODY_00_15(11, B, C, D, E, T, A, X(11));	X(13) = W[13];
                BODY_00_15(12, A, B, C, D, E, T, X(12));	X(14) = W[14];
                BODY_00_15(13, T, A, B, C, D, E, X(13));	X(15) = W[15];
                BODY_00_15(14, E, T, A, B, C, D, X(14));
                BODY_00_15(15, D, E, T, A, B, C, X(15));

                data += SHA_CBLOCK;
            }
            else
            {
                HOST_c2l(data, l); X(0) = l;		HOST_c2l(data, l); X(1) = l;
                BODY_00_15(0, A, B, C, D, E, T, X(0));	HOST_c2l(data, l); X(2) = l;
                BODY_00_15(1, T, A, B, C, D, E, X(1));	HOST_c2l(data, l); X(3) = l;
                BODY_00_15(2, E, T, A, B, C, D, X(2));	HOST_c2l(data, l); X(4) = l;
                BODY_00_15(3, D, E, T, A, B, C, X(3));	HOST_c2l(data, l); X(5) = l;
                BODY_00_15(4, C, D, E, T, A, B, X(4));	HOST_c2l(data, l); X(6) = l;
                BODY_00_15(5, B, C, D, E, T, A, X(5));	HOST_c2l(data, l); X(7) = l;
                BODY_00_15(6, A, B, C, D, E, T, X(6));	HOST_c2l(data, l); X(8) = l;
                BODY_00_15(7, T, A, B, C, D, E, X(7));	HOST_c2l(data, l); X(9) = l;
                BODY_00_15(8, E, T, A, B, C, D, X(8));	HOST_c2l(data, l); X(10) = l;
                BODY_00_15(9, D, E, T, A, B, C, X(9));	HOST_c2l(data, l); X(11) = l;
                BODY_00_15(10, C, D, E, T, A, B, X(10));	HOST_c2l(data, l); X(12) = l;
                BODY_00_15(11, B, C, D, E, T, A, X(11));	HOST_c2l(data, l); X(13) = l;
                BODY_00_15(12, A, B, C, D, E, T, X(12));	HOST_c2l(data, l); X(14) = l;
                BODY_00_15(13, T, A, B, C, D, E, X(13));	HOST_c2l(data, l); X(15) = l;
                BODY_00_15(14, E, T, A, B, C, D, X(14));
                BODY_00_15(15, D, E, T, A, B, C, X(15));
            }

            BODY_16_19(16, C, D, E, T, A, B, X(0), X(0), X(2), X(8), X(13));
            BODY_16_19(17, B, C, D, E, T, A, X(1), X(1), X(3), X(9), X(14));
            BODY_16_19(18, A, B, C, D, E, T, X(2), X(2), X(4), X(10), X(15));
            BODY_16_19(19, T, A, B, C, D, E, X(3), X(3), X(5), X(11), X(0));

            BODY_20_31(20, E, T, A, B, C, D, X(4), X(4), X(6), X(12), X(1));
            BODY_20_31(21, D, E, T, A, B, C, X(5), X(5), X(7), X(13), X(2));
            BODY_20_31(22, C, D, E, T, A, B, X(6), X(6), X(8), X(14), X(3));
            BODY_20_31(23, B, C, D, E, T, A, X(7), X(7), X(9), X(15), X(4));
            BODY_20_31(24, A, B, C, D, E, T, X(8), X(8), X(10), X(0), X(5));
            BODY_20_31(25, T, A, B, C, D, E, X(9), X(9), X(11), X(1), X(6));
            BODY_20_31(26, E, T, A, B, C, D, X(10), X(10), X(12), X(2), X(7));
            BODY_20_31(27, D, E, T, A, B, C, X(11), X(11), X(13), X(3), X(8));
            BODY_20_31(28, C, D, E, T, A, B, X(12), X(12), X(14), X(4), X(9));
            BODY_20_31(29, B, C, D, E, T, A, X(13), X(13), X(15), X(5), X(10));
            BODY_20_31(30, A, B, C, D, E, T, X(14), X(14), X(0), X(6), X(11));
            BODY_20_31(31, T, A, B, C, D, E, X(15), X(15), X(1), X(7), X(12));

            BODY_32_39(32, E, T, A, B, C, D, X(0), X(2), X(8), X(13));
            BODY_32_39(33, D, E, T, A, B, C, X(1), X(3), X(9), X(14));
            BODY_32_39(34, C, D, E, T, A, B, X(2), X(4), X(10), X(15));
            BODY_32_39(35, B, C, D, E, T, A, X(3), X(5), X(11), X(0));
            BODY_32_39(36, A, B, C, D, E, T, X(4), X(6), X(12), X(1));
            BODY_32_39(37, T, A, B, C, D, E, X(5), X(7), X(13), X(2));
            BODY_32_39(38, E, T, A, B, C, D, X(6), X(8), X(14), X(3));
            BODY_32_39(39, D, E, T, A, B, C, X(7), X(9), X(15), X(4));

            BODY_40_59(40, C, D, E, T, A, B, X(8), X(10), X(0), X(5));
            BODY_40_59(41, B, C, D, E, T, A, X(9), X(11), X(1), X(6));
            BODY_40_59(42, A, B, C, D, E, T, X(10), X(12), X(2), X(7));
            BODY_40_59(43, T, A, B, C, D, E, X(11), X(13), X(3), X(8));
            BODY_40_59(44, E, T, A, B, C, D, X(12), X(14), X(4), X(9));
            BODY_40_59(45, D, E, T, A, B, C, X(13), X(15), X(5), X(10));
            BODY_40_59(46, C, D, E, T, A, B, X(14), X(0), X(6), X(11));
            BODY_40_59(47, B, C, D, E, T, A, X(15), X(1), X(7), X(12));
            BODY_40_59(48, A, B, C, D, E, T, X(0), X(2), X(8), X(13));
            BODY_40_59(49, T, A, B, C, D, E, X(1), X(3), X(9), X(14));
            BODY_40_59(50, E, T, A, B, C, D, X(2), X(4), X(10), X(15));
            BODY_40_59(51, D, E, T, A, B, C, X(3), X(5), X(11), X(0));
            BODY_40_59(52, C, D, E, T, A, B, X(4), X(6), X(12), X(1));
            BODY_40_59(53, B, C, D, E, T, A, X(5), X(7), X(13), X(2));
            BODY_40_59(54, A, B, C, D, E, T, X(6), X(8), X(14), X(3));
            BODY_40_59(55, T, A, B, C, D, E, X(7), X(9), X(15), X(4));
            BODY_40_59(56, E, T, A, B, C, D, X(8), X(10), X(0), X(5));
            BODY_40_59(57, D, E, T, A, B, C, X(9), X(11), X(1), X(6));
            BODY_40_59(58, C, D, E, T, A, B, X(10), X(12), X(2), X(7));
            BODY_40_59(59, B, C, D, E, T, A, X(11), X(13), X(3), X(8));

            BODY_60_79(60, A, B, C, D, E, T, X(12), X(14), X(4), X(9));
            BODY_60_79(61, T, A, B, C, D, E, X(13), X(15), X(5), X(10));
            BODY_60_79(62, E, T, A, B, C, D, X(14), X(0), X(6), X(11));
            BODY_60_79(63, D, E, T, A, B, C, X(15), X(1), X(7), X(12));
            BODY_60_79(64, C, D, E, T, A, B, X(0), X(2), X(8), X(13));
            BODY_60_79(65, B, C, D, E, T, A, X(1), X(3), X(9), X(14));
            BODY_60_79(66, A, B, C, D, E, T, X(2), X(4), X(10), X(15));
            BODY_60_79(67, T, A, B, C, D, E, X(3), X(5), X(11), X(0));
            BODY_60_79(68, E, T, A, B, C, D, X(4), X(6), X(12), X(1));
            BODY_60_79(69, D, E, T, A, B, C, X(5), X(7), X(13), X(2));
            BODY_60_79(70, C, D, E, T, A, B, X(6), X(8), X(14), X(3));
            BODY_60_79(71, B, C, D, E, T, A, X(7), X(9), X(15), X(4));
            BODY_60_79(72, A, B, C, D, E, T, X(8), X(10), X(0), X(5));
            BODY_60_79(73, T, A, B, C, D, E, X(9), X(11), X(1), X(6));
            BODY_60_79(74, E, T, A, B, C, D, X(10), X(12), X(2), X(7));
            BODY_60_79(75, D, E, T, A, B, C, X(11), X(13), X(3), X(8));
            BODY_60_79(76, C, D, E, T, A, B, X(12), X(14), X(4), X(9));
            BODY_60_79(77, B, C, D, E, T, A, X(13), X(15), X(5), X(10));
            BODY_60_79(78, A, B, C, D, E, T, X(14), X(0), X(6), X(11));
            BODY_60_79(79, T, A, B, C, D, E, X(15), X(1), X(7), X(12));

            c->h0 = (c->h0 + E) & 0xffffffffL;
            c->h1 = (c->h1 + T) & 0xffffffffL;
            c->h2 = (c->h2 + A) & 0xffffffffL;
            c->h3 = (c->h3 + B) & 0xffffffffL;
            c->h4 = (c->h4 + C) & 0xffffffffL;

            if (--num == 0) break;

            A = c->h0;
            B = c->h1;
            C = c->h2;
            D = c->h3;
            E = c->h4;
        }
#endif
    }

    static int SHA1_Init(SHA_CTX *c)
    {
        memset(c, 0, sizeof(*c));
        c->h0 = 0x67452301UL;
        c->h1 = 0xefcdab89UL;
        c->h2 = 0x98badcfeUL;
        c->h3 = 0x10325476UL;
        c->h4 = 0xc3d2e1f0UL;
        return 1;
    }

    static int SHA1_Update(SHA_CTX *c, const void *data_, size_t len)
    {
        const unsigned char *data = (const unsigned char *)data_;
        unsigned char *p;
        SHA_LONG l;
        size_t n;

        if (len == 0) return 1;

        l = (c->Nl + (((SHA_LONG)len) << 3)) & 0xffffffffUL;
        /* 95-05-24 eay Fixed a bug with the overflow handling, thanks to
        * Wei Dai <weidai@eskimo.com> for pointing it out. */
        if (l < c->Nl) /* overflow */
            c->Nh++;
        c->Nh += (SHA_LONG)(len >> 29);	/* might cause compiler warning on 16-bit */
        c->Nl = l;

        n = c->num;
        if (n != 0)
        {
            p = (unsigned char *)c->data;

            if (len >= SHA_CBLOCK || len + n >= SHA_CBLOCK)
            {
                memcpy(p + n, data, SHA_CBLOCK - n);
                sha1_block_data_order(c, p, 1);
                n = SHA_CBLOCK - n;
                data += n;
                len -= n;
                c->num = 0;
                memset(p, 0, SHA_CBLOCK);	/* keep it zeroed */
            }
            else
            {
                memcpy(p + n, data, len);
                c->num += (unsigned int)len;
                return 1;
            }
        }

        n = len / SHA_CBLOCK;
        if (n > 0)
        {
            sha1_block_data_order(c, data, n);
            n *= SHA_CBLOCK;
            data += n;
            len -= n;
        }

        if (len != 0)
        {
            p = (unsigned char *)c->data;
            c->num = (unsigned int)len;
            memcpy(p, data, len);
        }
        return 1;
    }

    static int SHA1_Final(unsigned char *md, SHA_CTX *c)
    {
        unsigned char *p = (unsigned char *)c->data;
        size_t n = c->num;

        p[n] = 0x80; /* there is always room for one */
        n++;

        if (n > (SHA_CBLOCK - 8))
        {
            memset(p + n, 0, SHA_CBLOCK - n);
            n = 0;
            sha1_block_data_order(c, p, 1);
        }
        memset(p + n, 0, SHA_CBLOCK - 8 - n);

        p += SHA_CBLOCK - 8;
        HOST_l2c(c->Nh, p);
        HOST_l2c(c->Nl, p);
        p -= SHA_CBLOCK;
        sha1_block_data_order(c, p, 1);
        c->num = 0;
        memset(p, 0, SHA_CBLOCK);

        SHA_MAKE_STRING(c, md);
        return 1;
    }

    /* SHA 256 */

#define	SHA256_MAKE_STRING(c,s)	do {	\
    unsigned long ll;		\
    unsigned int  nn;		\
    switch ((c)->md_len)		\
    {   case SHA224_DIGEST_LENGTH:	\
    for (nn=0;nn<SHA224_DIGEST_LENGTH/4;nn++)	\
    {   ll=(c)->h[nn]; HOST_l2c(ll,(s));   }	\
    break;			\
    case SHA256_DIGEST_LENGTH:	\
    for (nn=0;nn<SHA256_DIGEST_LENGTH/4;nn++)	\
    {   ll=(c)->h[nn]; HOST_l2c(ll,(s));   }	\
    break;			\
    default:			\
    if ((c)->md_len > SHA256_DIGEST_LENGTH)	\
    return 0;				\
    for (nn=0;nn<(c)->md_len/4;nn++)		\
    {   ll=(c)->h[nn]; HOST_l2c(ll,(s));   }	\
    break;			\
    }				\
    } while (0)

    static const SHA_LONG K256[64] = {
        0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
        0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
        0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
        0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
        0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
        0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
        0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
        0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
        0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
        0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
        0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
        0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
        0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
        0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
        0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
        0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL };

#define Sigma0(x)	(ROTATE((x),30) ^ ROTATE((x),19) ^ ROTATE((x),10))
#define Sigma1(x)	(ROTATE((x),26) ^ ROTATE((x),21) ^ ROTATE((x),7))
#define sigma0(x)	(ROTATE((x),25) ^ ROTATE((x),14) ^ ((x)>>3))
#define sigma1(x)	(ROTATE((x),15) ^ ROTATE((x),13) ^ ((x)>>10))
#define Ch(x,y,z)	(((x) & (y)) ^ ((~(x)) & (z)))
#define Maj(x,y,z)	(((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))

#if defined ARMV8_CRYPT || defined ARMV7_CRYPT 
extern "C" void sha256_block_data_order(SHA256_CTX* ctx, const void* in, size_t num);
#else
    static void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num)
    {
        unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1, T2;
        SHA_LONG	X[16], l;
        int i;
        const unsigned char *data = (const unsigned char *)in;

        while (num--) {

            a = ctx->h[0];	b = ctx->h[1];	c = ctx->h[2];	d = ctx->h[3];
            e = ctx->h[4];	f = ctx->h[5];	g = ctx->h[6];	h = ctx->h[7];

            for (i = 0; i<16; i++)
            {
                HOST_c2l(data, l); T1 = X[i] = l;
                T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i];
                T2 = Sigma0(a) + Maj(a, b, c);
                h = g;	g = f;	f = e;	e = d + T1;
                d = c;	c = b;	b = a;	a = T1 + T2;
            }

            for (; i<64; i++)
            {
                s0 = X[(i + 1) & 0x0f];	s0 = sigma0(s0);
                s1 = X[(i + 14) & 0x0f];	s1 = sigma1(s1);

                T1 = X[i & 0xf] += s0 + s1 + X[(i + 9) & 0xf];
                T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i];
                T2 = Sigma0(a) + Maj(a, b, c);
                h = g;	g = f;	f = e;	e = d + T1;
                d = c;	c = b;	b = a;	a = T1 + T2;
            }

            ctx->h[0] += a;	ctx->h[1] += b;	ctx->h[2] += c;	ctx->h[3] += d;
            ctx->h[4] += e;	ctx->h[5] += f;	ctx->h[6] += g;	ctx->h[7] += h;

        }
    }
#endif
    static int SHA256_Init(SHA256_CTX *c)
    {
        memset(c, 0, sizeof(*c));
        c->h[0] = 0x6a09e667UL;	c->h[1] = 0xbb67ae85UL;
        c->h[2] = 0x3c6ef372UL;	c->h[3] = 0xa54ff53aUL;
        c->h[4] = 0x510e527fUL;	c->h[5] = 0x9b05688cUL;
        c->h[6] = 0x1f83d9abUL;	c->h[7] = 0x5be0cd19UL;
        c->md_len = SHA256_DIGEST_LENGTH;
        return 1;
    }

    static int SHA256_Update(SHA256_CTX *c, const void *data_, size_t len)
    {
        const unsigned char *data = (const unsigned char *)data_;
        unsigned char *p;
        SHA_LONG l;
        size_t n;

        if (len == 0) return 1;

        l = (c->Nl + (((SHA_LONG)len) << 3)) & 0xffffffffUL;
        if (l < c->Nl)
            c->Nh++;
        c->Nh += (SHA_LONG)(len >> 29);
        c->Nl = l;

        n = c->num;
        if (n != 0)
        {
            p = (unsigned char *)c->data;

            if (len >= SHA_CBLOCK || len + n >= SHA_CBLOCK)
            {
                memcpy(p + n, data, SHA_CBLOCK - n);
                sha256_block_data_order(c, p, 1);
                n = SHA_CBLOCK - n;
                data += n;
                len -= n;
                c->num = 0;
                memset(p, 0, SHA_CBLOCK);	/* keep it zeroed */
            }
            else
            {
                memcpy(p + n, data, len);
                c->num += (unsigned int)len;
                return 1;
            }
        }

        n = len / SHA_CBLOCK;
        if (n > 0)
        {
            sha256_block_data_order(c, data, n);
            n *= SHA_CBLOCK;
            data += n;
            len -= n;
        }

        if (len != 0)
        {
            p = (unsigned char *)c->data;
            c->num = (unsigned int)len;
            memcpy(p, data, len);
        }
        return 1;
    }

    static int SHA256_Final(unsigned char *md, SHA256_CTX *c)
    {
        unsigned char *p = (unsigned char *)c->data;
        size_t n = c->num;

        p[n] = 0x80; /* there is always room for one */
        n++;

        if (n > (SHA_CBLOCK - 8))
        {
            memset(p + n, 0, SHA_CBLOCK - n);
            n = 0;
            sha256_block_data_order(c, p, 1);
        }
        memset(p + n, 0, SHA_CBLOCK - 8 - n);

        p += SHA_CBLOCK - 8;
        HOST_l2c(c->Nh, p);
        HOST_l2c(c->Nl, p);
        p -= SHA_CBLOCK;
        sha256_block_data_order(c, p, 1);
        c->num = 0;
        memset(p, 0, SHA_CBLOCK);

        SHA256_MAKE_STRING(c, md);
        return 1;
    }

#undef Sigma0
#undef Sigma1
#undef sigma0
#undef sigma1
#undef Ch
#undef Maj

    /* SHA 224 */
    static int SHA224_Init(SHA256_CTX *c)
    {
        memset(c, 0, sizeof(*c));
        c->h[0] = 0xc1059ed8UL;	c->h[1] = 0x367cd507UL;
        c->h[2] = 0x3070dd17UL;	c->h[3] = 0xf70e5939UL;
        c->h[4] = 0xffc00b31UL;	c->h[5] = 0x68581511UL;
        c->h[6] = 0x64f98fa7UL;	c->h[7] = 0xbefa4fa4UL;
        c->md_len = SHA224_DIGEST_LENGTH;
        return 1;
    }

    static int SHA224_Update(SHA256_CTX *c, const void *data, size_t len)
    {
        return SHA256_Update(c, data, len);
    }

    static int SHA224_Final(unsigned char *md, SHA256_CTX *c)
    {
        return SHA256_Final(md, c);
    }

    /* SHA 512 */

    static const SHA_LONG64 K512[80] = {
        U64(0x428a2f98d728ae22), U64(0x7137449123ef65cd),
        U64(0xb5c0fbcfec4d3b2f), U64(0xe9b5dba58189dbbc),
        U64(0x3956c25bf348b538), U64(0x59f111f1b605d019),
        U64(0x923f82a4af194f9b), U64(0xab1c5ed5da6d8118),
        U64(0xd807aa98a3030242), U64(0x12835b0145706fbe),
        U64(0x243185be4ee4b28c), U64(0x550c7dc3d5ffb4e2),
        U64(0x72be5d74f27b896f), U64(0x80deb1fe3b1696b1),
        U64(0x9bdc06a725c71235), U64(0xc19bf174cf692694),
        U64(0xe49b69c19ef14ad2), U64(0xefbe4786384f25e3),
        U64(0x0fc19dc68b8cd5b5), U64(0x240ca1cc77ac9c65),
        U64(0x2de92c6f592b0275), U64(0x4a7484aa6ea6e483),
        U64(0x5cb0a9dcbd41fbd4), U64(0x76f988da831153b5),
        U64(0x983e5152ee66dfab), U64(0xa831c66d2db43210),
        U64(0xb00327c898fb213f), U64(0xbf597fc7beef0ee4),
        U64(0xc6e00bf33da88fc2), U64(0xd5a79147930aa725),
        U64(0x06ca6351e003826f), U64(0x142929670a0e6e70),
        U64(0x27b70a8546d22ffc), U64(0x2e1b21385c26c926),
        U64(0x4d2c6dfc5ac42aed), U64(0x53380d139d95b3df),
        U64(0x650a73548baf63de), U64(0x766a0abb3c77b2a8),
        U64(0x81c2c92e47edaee6), U64(0x92722c851482353b),
        U64(0xa2bfe8a14cf10364), U64(0xa81a664bbc423001),
        U64(0xc24b8b70d0f89791), U64(0xc76c51a30654be30),
        U64(0xd192e819d6ef5218), U64(0xd69906245565a910),
        U64(0xf40e35855771202a), U64(0x106aa07032bbd1b8),
        U64(0x19a4c116b8d2d0c8), U64(0x1e376c085141ab53),
        U64(0x2748774cdf8eeb99), U64(0x34b0bcb5e19b48a8),
        U64(0x391c0cb3c5c95a63), U64(0x4ed8aa4ae3418acb),
        U64(0x5b9cca4f7763e373), U64(0x682e6ff3d6b2b8a3),
        U64(0x748f82ee5defb2fc), U64(0x78a5636f43172f60),
        U64(0x84c87814a1f0ab72), U64(0x8cc702081a6439ec),
        U64(0x90befffa23631e28), U64(0xa4506cebde82bde9),
        U64(0xbef9a3f7b2c67915), U64(0xc67178f2e372532b),
        U64(0xca273eceea26619c), U64(0xd186b8c721c0c207),
        U64(0xeada7dd6cde0eb1e), U64(0xf57d4f7fee6ed178),
        U64(0x06f067aa72176fba), U64(0x0a637dc5a2c898a6),
        U64(0x113f9804bef90dae), U64(0x1b710b35131c471b),
        U64(0x28db77f523047d84), U64(0x32caab7b40c72493),
        U64(0x3c9ebe0a15c9bebc), U64(0x431d67c49c100d4c),
        U64(0x4cc5d4becb3e42b6), U64(0x597f299cfc657e2a),
        U64(0x5fcb6fab3ad6faec), U64(0x6c44198c4a475817) };

#define B(x,j)    (((SHA_LONG64)(*(((const unsigned char *)(&x))+j)))<<((7-j)*8))
#define PULL64(x) (B(x,0)|B(x,1)|B(x,2)|B(x,3)|B(x,4)|B(x,5)|B(x,6)|B(x,7))
#define ROTR(x,s)	(((x)>>s) | (x)<<(64-s))
#define Sigma0(x)	(ROTR((x),28) ^ ROTR((x),34) ^ ROTR((x),39))
#define Sigma1(x)	(ROTR((x),14) ^ ROTR((x),18) ^ ROTR((x),41))
#define sigma0(x)	(ROTR((x),1)  ^ ROTR((x),8)  ^ ((x)>>7))
#define sigma1(x)	(ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6))
#define Ch(x,y,z)	(((x) & (y)) ^ ((~(x)) & (z)))
#define Maj(x,y,z)	(((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))

#if defined ARMV8_CRYPT || defined ARMV7_CRYPT 
extern "C" void sha512_block_data_order(SHA512_CTX* ctx, const void* in, size_t num);
#else
    static void sha512_block_data_order(SHA512_CTX *ctx, const void *in, size_t num)
    {
        const SHA_LONG64 * W = (const SHA_LONG64 *)in;
        SHA_LONG64	a, b, c, d, e, f, g, h, s0, s1, T1, T2;
        SHA_LONG64	X[16];
        int i;

        while (num--) {

            a = ctx->h[0];	b = ctx->h[1];	c = ctx->h[2];	d = ctx->h[3];
            e = ctx->h[4];	f = ctx->h[5];	g = ctx->h[6];	h = ctx->h[7];

            for (i = 0; i<16; i++)
            {
#ifdef B_ENDIAN
                T1 = X[i] = W[i];
#else
                T1 = X[i] = PULL64(W[i]);
#endif
                T1 += h + Sigma1(e) + Ch(e, f, g) + K512[i];
                T2 = Sigma0(a) + Maj(a, b, c);
                h = g;	g = f;	f = e;	e = d + T1;
                d = c;	c = b;	b = a;	a = T1 + T2;
            }

            for (; i<80; i++)
            {
                s0 = X[(i + 1) & 0x0f];	s0 = sigma0(s0);
                s1 = X[(i + 14) & 0x0f];	s1 = sigma1(s1);

                T1 = X[i & 0xf] += s0 + s1 + X[(i + 9) & 0xf];
                T1 += h + Sigma1(e) + Ch(e, f, g) + K512[i];
                T2 = Sigma0(a) + Maj(a, b, c);
                h = g;	g = f;	f = e;	e = d + T1;
                d = c;	c = b;	b = a;	a = T1 + T2;
            }

            ctx->h[0] += a;	ctx->h[1] += b;	ctx->h[2] += c;	ctx->h[3] += d;
            ctx->h[4] += e;	ctx->h[5] += f;	ctx->h[6] += g;	ctx->h[7] += h;

            W += SHA_LBLOCK;
        }
    }
#endif

    static int SHA512_Init(SHA512_CTX *c)
    {
        c->h[0] = 0x6a09e667f3bcc908ULL;
        c->h[1] = 0xbb67ae8584caa73bULL;
        c->h[2] = 0x3c6ef372fe94f82bULL;
        c->h[3] = 0xa54ff53a5f1d36f1ULL;
        c->h[4] = 0x510e527fade682d1ULL;
        c->h[5] = 0x9b05688c2b3e6c1fULL;
        c->h[6] = 0x1f83d9abfb41bd6bULL;
        c->h[7] = 0x5be0cd19137e2179ULL;
        c->Nl = 0;        c->Nh = 0;
        c->num = 0;       c->md_len = SHA512_DIGEST_LENGTH;
        return 1;
    }

    static int SHA512_224_Init(SHA512_CTX * c)
    {
        c->h[0] = U64(0x8c3d37c819544da2);
        c->h[1] = U64(0x73e1996689dcd4d6);
        c->h[2] = U64(0x1dfab7ae32ff9c82);
        c->h[3] = U64(0x679dd514582f9fcf);
        c->h[4] = U64(0x0f6d2b697bd44da8);
        c->h[5] = U64(0x77e36f7304c48942);
        c->h[6] = U64(0x3f9d85a86a1d36c8);
        c->h[7] = U64(0x1112e6ad91d692a1);

        c->Nl = 0;
        c->Nh = 0;
        c->num = 0;
        c->md_len = SHA224_DIGEST_LENGTH;
        return 1;
    }

    static int SHA512_256_Init(SHA512_CTX * c)
    {
        c->h[0] = U64(0x22312194fc2bf72c);
        c->h[1] = U64(0x9f555fa3c84c64c2);
        c->h[2] = U64(0x2393b86b6f53b151);
        c->h[3] = U64(0x963877195940eabd);
        c->h[4] = U64(0x96283ee2a88effe3);
        c->h[5] = U64(0xbe5e1e2553863992);
        c->h[6] = U64(0x2b0199fc2c85b8aa);
        c->h[7] = U64(0x0eb72ddc81c52ca2);

        c->Nl = 0;
        c->Nh = 0;
        c->num = 0;
        c->md_len = SHA256_DIGEST_LENGTH;
        return 1;
    }

    static int SHA512_Update(SHA512_CTX *c, const void *_data, size_t len)
    {
        SHA_LONG64	l;
        unsigned char  *p = c->u.p;
        const unsigned char *data = (const unsigned char *)_data;

        if (len == 0) return  1;

        l = (c->Nl + (((SHA_LONG64)len) << 3))&U64(0xffffffffffffffff);
        if (l < c->Nl)		c->Nh++;
        if (sizeof(len) >= 8)	c->Nh += (((SHA_LONG64)len) >> 61);
        c->Nl = l;

        if (c->num != 0)
        {
            size_t n = sizeof(c->u) - c->num;

            if (len < n)
            {
                memcpy(p + c->num, data, len), c->num += (unsigned int)len;
                return 1;
            }
            else	{
                memcpy(p + c->num, data, n), c->num = 0;
                len -= n, data += n;
                sha512_block_data_order(c, p, 1);
            }
        }

        if (len >= sizeof(c->u))
        {
#ifndef SHA512_BLOCK_CAN_MANAGE_UNALIGNED_DATA
            if ((size_t)data%sizeof(c->u.d[0]) != 0)
            while (len >= sizeof(c->u))
                memcpy(p, data, sizeof(c->u)),
                sha512_block_data_order(c, p, 1),
                len -= sizeof(c->u),
                data += sizeof(c->u);
            else
#endif
                sha512_block_data_order(c, data, len / sizeof(c->u)),
                data += len,
                len %= sizeof(c->u),
                data -= len;
        }

        if (len != 0)	memcpy(p, data, len), c->num = (int)len;

        return 1;
    }

    static int SHA512_Final(unsigned char *md, SHA512_CTX *c)
    {
        unsigned char *p = (unsigned char *)c->u.p;
        size_t n = c->num;

        p[n] = 0x80;	/* There always is a room for one */
        n++;
        if (n > (sizeof(c->u) - 16))
            memset(p + n, 0, sizeof(c->u) - n), n = 0,
            sha512_block_data_order(c, p, 1);

        memset(p + n, 0, sizeof(c->u) - 16 - n);
#ifdef	B_ENDIAN
        c->u.d[SHA_LBLOCK - 2] = c->Nh;
        c->u.d[SHA_LBLOCK - 1] = c->Nl;
#else
        p[sizeof(c->u) - 1] = (unsigned char)(c->Nl);
        p[sizeof(c->u) - 2] = (unsigned char)(c->Nl >> 8);
        p[sizeof(c->u) - 3] = (unsigned char)(c->Nl >> 16);
        p[sizeof(c->u) - 4] = (unsigned char)(c->Nl >> 24);
        p[sizeof(c->u) - 5] = (unsigned char)(c->Nl >> 32);
        p[sizeof(c->u) - 6] = (unsigned char)(c->Nl >> 40);
        p[sizeof(c->u) - 7] = (unsigned char)(c->Nl >> 48);
        p[sizeof(c->u) - 8] = (unsigned char)(c->Nl >> 56);
        p[sizeof(c->u) - 9] = (unsigned char)(c->Nh);
        p[sizeof(c->u) - 10] = (unsigned char)(c->Nh >> 8);
        p[sizeof(c->u) - 11] = (unsigned char)(c->Nh >> 16);
        p[sizeof(c->u) - 12] = (unsigned char)(c->Nh >> 24);
        p[sizeof(c->u) - 13] = (unsigned char)(c->Nh >> 32);
        p[sizeof(c->u) - 14] = (unsigned char)(c->Nh >> 40);
        p[sizeof(c->u) - 15] = (unsigned char)(c->Nh >> 48);
        p[sizeof(c->u) - 16] = (unsigned char)(c->Nh >> 56);
#endif

        sha512_block_data_order(c, p, 1);

        if (md == 0) return 0;

        switch (c->md_len)
        {
            /* Let compiler decide if it's appropriate to unroll... */
        case SHA224_DIGEST_LENGTH:
            for (n = 0; n < SHA224_DIGEST_LENGTH / 8; n++) {
                SHA_LONG64 t = c->h[n];

                *(md++) = (unsigned char)(t >> 56);
                *(md++) = (unsigned char)(t >> 48);
                *(md++) = (unsigned char)(t >> 40);
                *(md++) = (unsigned char)(t >> 32);
                *(md++) = (unsigned char)(t >> 24);
                *(md++) = (unsigned char)(t >> 16);
                *(md++) = (unsigned char)(t >> 8);
                *(md++) = (unsigned char)(t);
            }
            /*
             * For 224 bits, there are four bytes left over that have to be
             * processed separately.
             */
            {
                SHA_LONG64 t = c->h[SHA224_DIGEST_LENGTH / 8];

                *(md++) = (unsigned char)(t >> 56);
                *(md++) = (unsigned char)(t >> 48);
                *(md++) = (unsigned char)(t >> 40);
                *(md++) = (unsigned char)(t >> 32);
            }
            break;
        case SHA256_DIGEST_LENGTH:
            for (n = 0; n < SHA256_DIGEST_LENGTH / 8; n++) {
                SHA_LONG64 t = c->h[n];

                *(md++) = (unsigned char)(t >> 56);
                *(md++) = (unsigned char)(t >> 48);
                *(md++) = (unsigned char)(t >> 40);
                *(md++) = (unsigned char)(t >> 32);
                *(md++) = (unsigned char)(t >> 24);
                *(md++) = (unsigned char)(t >> 16);
                *(md++) = (unsigned char)(t >> 8);
                *(md++) = (unsigned char)(t);
            }
            break;
        case SHA384_DIGEST_LENGTH:
            for (n = 0; n<SHA384_DIGEST_LENGTH / 8; n++)
            {
                SHA_LONG64 t = c->h[n];

                *(md++) = (unsigned char)(t >> 56);
                *(md++) = (unsigned char)(t >> 48);
                *(md++) = (unsigned char)(t >> 40);
                *(md++) = (unsigned char)(t >> 32);
                *(md++) = (unsigned char)(t >> 24);
                *(md++) = (unsigned char)(t >> 16);
                *(md++) = (unsigned char)(t >> 8);
                *(md++) = (unsigned char)(t);
            }
            break;
        case SHA512_DIGEST_LENGTH:
            for (n = 0; n<SHA512_DIGEST_LENGTH / 8; n++)
            {
                SHA_LONG64 t = c->h[n];

                *(md++) = (unsigned char)(t >> 56);
                *(md++) = (unsigned char)(t >> 48);
                *(md++) = (unsigned char)(t >> 40);
                *(md++) = (unsigned char)(t >> 32);
                *(md++) = (unsigned char)(t >> 24);
                *(md++) = (unsigned char)(t >> 16);
                *(md++) = (unsigned char)(t >> 8);
                *(md++) = (unsigned char)(t);
            }
            break;
            /* ... as well as make sure md_len is not abused. */
        default:	return 0;
        }

        return 1;
    }

    /* SHA 384 */

    static int SHA384_Init(SHA512_CTX *c)
    {
        c->h[0] = 0xcbbb9d5dc1059ed8ULL;
        c->h[1] = 0x629a292a367cd507ULL;
        c->h[2] = 0x9159015a3070dd17ULL;
        c->h[3] = 0x152fecd8f70e5939ULL;
        c->h[4] = 0x67332667ffc00b31ULL;
        c->h[5] = 0x8eb44a8768581511ULL;
        c->h[6] = 0xdb0c2e0d64f98fa7ULL;
        c->h[7] = 0x47b5481dbefa4fa4ULL;
        c->Nl = 0;        c->Nh = 0;
        c->num = 0;       c->md_len = SHA384_DIGEST_LENGTH;
        return 1;
    }

    static int SHA384_Update(SHA512_CTX *c, const void *data, size_t len)
    {
        return SHA512_Update(c, data, len);
    }

    static int SHA384_Final(unsigned char *md, SHA512_CTX *c)
    {
        return SHA512_Final(md, c);
    }
}

/* implementation of interface */

USING_NAMESPACE

hash::hash()
{
    type = HASH_UNDEFINED;
}

void hash::init(hash_t type)
{
    this->type = type;
    switch (type) {
    case HASH_MD4: _rsa_md4::MD4Init((_rsa_md4::MD4_CTX *) context); break;
    case HASH_MD5: _rsa_md5::MD5Init((_rsa_md5::MD5_CTX *) context); break;
    case HASH_SHA1: _openssl::SHA1_Init((_openssl::SHA_CTX *) context); break;
    case HASH_SHA224: _openssl::SHA224_Init((_openssl::SHA256_CTX *) context); break;
    case HASH_SHA256: _openssl::SHA256_Init((_openssl::SHA256_CTX *) context); break;
    case HASH_SHA384: _openssl::SHA384_Init((_openssl::SHA512_CTX *) context); break;
    case HASH_SHA512: _openssl::SHA512_Init((_openssl::SHA512_CTX *) context); break;
    case HASH_SHA512_224: _openssl::SHA512_224_Init((_openssl::SHA512_CTX *)context); break;
    case HASH_SHA512_256: _openssl::SHA512_256_Init((_openssl::SHA512_CTX *)context); break;
    case HASH_UNDEFINED: break;
    default: ASSERT(false, "hash::init not implemented"); break;
    }
}

void hash::update(const void * in, size_t len)
{
    switch (type) {
    case HASH_MD4: _rsa_md4::MD4Update((_rsa_md4::MD4_CTX *) context, (unsigned char *)in, (unsigned int)len); break;
    case HASH_MD5: _rsa_md5::MD5Update((_rsa_md5::MD5_CTX *) context, (unsigned char *)in, (unsigned int)len); break;
    case HASH_SHA1: _openssl::SHA1_Update((_openssl::SHA_CTX *) context, (unsigned char *)in, len); break;
    case HASH_SHA224: _openssl::SHA224_Update((_openssl::SHA256_CTX *) context, (unsigned char *)in, len); break;
    case HASH_SHA256: _openssl::SHA256_Update((_openssl::SHA256_CTX *) context, (unsigned char *)in, len); break;
    case HASH_SHA384: _openssl::SHA384_Update((_openssl::SHA512_CTX *) context, (unsigned char *)in, len); break;
    case HASH_SHA512: _openssl::SHA512_Update((_openssl::SHA512_CTX *) context, (unsigned char *)in, len); break;
    case HASH_SHA512_224: _openssl::SHA512_Update((_openssl::SHA512_CTX *)context, (unsigned char *)in, len); break;
    case HASH_SHA512_256: _openssl::SHA512_Update((_openssl::SHA512_CTX *)context, (unsigned char *)in, len); break;
    case HASH_UNDEFINED: break;
    default: ASSERT(false, "hash::update not implemented"); break;
    }
}

void hash::final(void * out)
{
    switch (type) {
    case HASH_MD4: _rsa_md4::MD4Final((unsigned char *)out, (_rsa_md4::MD4_CTX *) context); break;
    case HASH_MD5: _rsa_md5::MD5Final((unsigned char *)out, (_rsa_md5::MD5_CTX *) context); break;
    case HASH_SHA1: _openssl::SHA1_Final((unsigned char *)out, (_openssl::SHA_CTX *) context); break;
    case HASH_SHA224: _openssl::SHA224_Final((unsigned char *)out, (_openssl::SHA256_CTX *) context); break;
    case HASH_SHA256: _openssl::SHA256_Final((unsigned char *)out, (_openssl::SHA256_CTX *) context); break;
    case HASH_SHA384: _openssl::SHA384_Final((unsigned char *)out, (_openssl::SHA512_CTX *) context); break;
    case HASH_SHA512: _openssl::SHA512_Final((unsigned char *)out, (_openssl::SHA512_CTX *) context); break;
    case HASH_SHA512_224: _openssl::SHA512_Final((unsigned char *)out, (_openssl::SHA512_CTX *)context); break;
    case HASH_SHA512_256: _openssl::SHA512_Final((unsigned char *)out, (_openssl::SHA512_CTX *)context); break;
    case HASH_UNDEFINED: break;
    default: ASSERT(false, "hash::Final not implemented"); break;
    }
}

void hash::copy(class hash * from)
{
    ASSERT(from, "hash::copy null pointer");
    memcpy(this, from, sizeof(hash));
}

size_t hash::size()
{
    switch (type) {
    case HASH_MD4: return 16;
    case HASH_MD5: return 16;
    case HASH_SHA1: return 20;
    case HASH_SHA224: return 28;
    case HASH_SHA256: return 32;
    case HASH_SHA384: return 48;
    case HASH_SHA512: return 64;
    case HASH_SHA512_224: return 28;
    case HASH_SHA512_256: return 32;
    case HASH_UNDEFINED: return 0;
    default: ASSERT(false, "hash not initialized");
    }
    return 0;
}

size_t hash::size(hash_t type)
{
    switch (type) {
    case HASH_MD4: return 16;
    case HASH_MD5: return 16;
    case HASH_SHA1: return 20;
    case HASH_SHA224: return 28;
    case HASH_SHA256: return 32;
    case HASH_SHA384: return 48;
    case HASH_SHA512: return 64;
    case HASH_SHA512_224: return 28;
    case HASH_SHA512_256: return 32;
    default: return 0;
    }
    return 0;
}

hmac::hmac()
{
    type = HASH_UNDEFINED;
}

void hmac::init(hash_t type, const void * key, size_t len)
{
    this->type = type;
    if (type == HASH_UNDEFINED) return;
    size_t block_size = type == HASH_SHA384 || type == HASH_SHA512 || type == HASH_SHA512_224 || type == HASH_SHA512_256 ? 128 : 64;
    ulong64 a1[16];
    a1[0] = 0;
    a1[1] = 0;
    a1[2] = 0;
    a1[3] = 0;
    a1[4] = 0;
    a1[5] = 0;
    a1[6] = 0;
    a1[7] = 0;
    if (block_size == 128) {
        a1[8] = 0;
        a1[9] = 0;
        a1[10] = 0;
        a1[11] = 0;
        a1[12] = 0;
        a1[13] = 0;
        a1[14] = 0;
        a1[15] = 0;
    }
    if (len > block_size) {
        h.init(type);
        h.update(key, len);
        h.final(a1);
    }
    else {
        memcpy(a1, key, len);
    }
    a2[0] = a1[0] ^ 0x5c5c5c5c5c5c5c5cull;
    a2[1] = a1[1] ^ 0x5c5c5c5c5c5c5c5cull;
    a2[2] = a1[2] ^ 0x5c5c5c5c5c5c5c5cull;
    a2[3] = a1[3] ^ 0x5c5c5c5c5c5c5c5cull;
    a2[4] = a1[4] ^ 0x5c5c5c5c5c5c5c5cull;
    a2[5] = a1[5] ^ 0x5c5c5c5c5c5c5c5cull;
    a2[6] = a1[6] ^ 0x5c5c5c5c5c5c5c5cull;
    a2[7] = a1[7] ^ 0x5c5c5c5c5c5c5c5cull;
    a1[0] = a1[0] ^ 0x3636363636363636ull;
    a1[1] = a1[1] ^ 0x3636363636363636ull;
    a1[2] = a1[2] ^ 0x3636363636363636ull;
    a1[3] = a1[3] ^ 0x3636363636363636ull;
    a1[4] = a1[4] ^ 0x3636363636363636ull;
    a1[5] = a1[5] ^ 0x3636363636363636ull;
    a1[6] = a1[6] ^ 0x3636363636363636ull;
    a1[7] = a1[7] ^ 0x3636363636363636ull;
    if (block_size == 128) {
        a2[8] = a1[8] ^ 0x5c5c5c5c5c5c5c5cull;
        a2[9] = a1[9] ^ 0x5c5c5c5c5c5c5c5cull;
        a2[10] = a1[10] ^ 0x5c5c5c5c5c5c5c5cull;
        a2[11] = a1[11] ^ 0x5c5c5c5c5c5c5c5cull;
        a2[12] = a1[12] ^ 0x5c5c5c5c5c5c5c5cull;
        a2[13] = a1[13] ^ 0x5c5c5c5c5c5c5c5cull;
        a2[14] = a1[14] ^ 0x5c5c5c5c5c5c5c5cull;
        a2[15] = a1[15] ^ 0x5c5c5c5c5c5c5c5cull;
        a1[8] = a1[8] ^ 0x3636363636363636ull;
        a1[9] = a1[9] ^ 0x3636363636363636ull;
        a1[10] = a1[10] ^ 0x3636363636363636ull;
        a1[11] = a1[11] ^ 0x3636363636363636ull;
        a1[12] = a1[12] ^ 0x3636363636363636ull;
        a1[13] = a1[13] ^ 0x3636363636363636ull;
        a1[14] = a1[14] ^ 0x3636363636363636ull;
        a1[15] = a1[15] ^ 0x3636363636363636ull;
    }
    h.init(type);
    h.update(a1, block_size);
}

void hmac::update(const void * in, size_t len)
{
    if (type == HASH_UNDEFINED) return;
    h.update(in, len);
}

void hmac::final(void * out)
{
    if (type == HASH_UNDEFINED) return;
    h.final(out);
    hash b;
    b.init(type);
    b.update(a2, type == HASH_SHA384 || type == HASH_SHA512 || type == HASH_SHA512_224 || type == HASH_SHA512_256 ? 128 : 64);
    b.update(out, size());
    b.final(out);
}

void hmac::copy(class hmac * from)
{
    ASSERT(from, "hmac::copy null pointer");
    memcpy(this, from, sizeof(hmac));
}

size_t hmac::size()
{
    return h.size();
}
