/**
* @file CM_MD5.c
* @brief MD5计算
* @details 参照 RFC-1321 标准实现
* @author GT-BU
* @date 2021.05.01
* @version V1.0
* @copyright TCT
* @par 修改日志
* <table>
* <tr><th>Date        <th>Version  <th>Author     <th>Description
* <tr><td>2021.05.01  <td>V1.0     <td>GT-BU      <td>创建初始版本
* </table>
*/
#include "CM_MD5.h"
#include "CM_Memory.h"
#include "PFM_Log.h"
static CM_UINT8 s_PADDING[64u] =
{
 0x80u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
    0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
    0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
};

/* 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) >> ((CM_UINT32)32U-(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) + (CM_UINT32)(ac);       \
        (a) = ROTATE_LEFT((a), (CM_UINT32)(s));                \
        (a) += (b);                                            \
    }

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

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

#define II(a, b, c, d, x, s, ac)                               \
    {                                                          \
        (a) += I((b), (c), (d)) + (x) + (CM_UINT32)(ac);       \
        (a) = ROTATE_LEFT((a), (CM_UINT32)(s));                \
        (a) += (b);                                            \
    }
static void s_MD5_Encode(OUT CM_UINT8 * output, IN const CM_UINT32 * input, IN CM_UINT32 len);
static void s_MD5_Decode(OUT CM_UINT32 output[], IN const CM_UINT8 input[], IN CM_UINT32 len);
static void s_MD5_Transform(INOUT CM_UINT32 state[4], IN const CM_UINT8 block[64]);
/**
* @brief 数据编码
* @param output 输出指针
* @param input 输入指针
* @param len 输入长度
* @details len必须是4的倍数
*/
static void s_MD5_Encode(OUT CM_UINT8 * output, IN const CM_UINT32 * input, IN CM_UINT32 len)
{
    /*- 变量初始化 */
    CM_UINT32 i = 0U;
    CM_INT32 j = 0;

    /*- @alias 将U32编码到U8数组 */
    for (i = (CM_UINT32)0u; (CM_UINT32)j < len; ++i)
    {
        output[j] = (CM_UINT8)(input[i] & 0xffU);
        output[j + 1] = (CM_UINT8)((input[i] >> 8U) & 0xffU);
        output[j + 2] = (CM_UINT8)((input[i] >> 16U) & 0xffU);
        output[j + 3] = (CM_UINT8)((input[i] >> 24U) & 0xffU);
        j += 4;
    }
}

/**
* @brief 数据解码
* @param output 输出指针
* @param input 输入指针
* @param len 输入长度
* @details len必须是4的倍数
*/
static void s_MD5_Decode(OUT CM_UINT32 output[], IN const CM_UINT8 input[], IN CM_UINT32 len)
{
    CM_UINT32 i = 0U;
    CM_INT32 j = 0;

    /*- @alias 从U8解码到U32数组 */
    for (i = (CM_UINT32)0u ; (CM_UINT32)j < len; ++i)
    {
        output[i] = ((CM_UINT32)input[j]) |
            (((CM_UINT32)input[j + 1]) << 8U) |
            (((CM_UINT32)input[j + 2]) << 16U) |
            (((CM_UINT32)input[j + 3]) << 24U);
        j += 4;
    }
}

/**
* @brief MD5变换
* @param state Context状态字
* @param block 数据块
*/
static void s_MD5_Transform(INOUT CM_UINT32 state[4], IN const CM_UINT8 block[64])
{
	/* Constants for MD5Transform routine. */
	static const int S11 = 7;
	static const int S12 = 12;
	static const int S13 = 17;
	static const int S14 = 22;
	static const int S21 = 5;
	static const int S22 = 9;
	static const int S23 = 14;
	static const int S24 = 20;
	static const int S31 = 4;
	static const int S32 = 11;
	static const int S33 = 16;
	static const int S34 = 23;
	static const int S41 = 6;
	static const int S42 = 10;
	static const int S43 = 15;
	static const int S44 = 21;

    CM_UINT32 a = 0U;
    CM_UINT32 b = 0U;
    CM_UINT32 c = 0U;
    CM_UINT32 d = 0U;
    CM_UINT32 x[16] = { 0u };

    a = state[0];

    b = state[1];
    c = state[2];
    d = state[3];

    /*- 将数据块编码 */
    s_MD5_Decode(x, block, (CM_UINT32)64u);

    /*- MD5 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, 0x242070dbU);    /* 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, 0x4787c62aU);    /* 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, 0x698098d8U);    /* 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, 0x6b901122U);   /* 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, 0x49b40821U);   /* 16 */

    /*- MD5 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, 0x265e5a51U);   /* 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, 0x2441453U);    /* 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, 0x21e1cde6U);    /* 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, 0x455a14edU);    /* 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, 0x676f02d9U);    /* 31 */

    GG(b, c, d, a, x[12], S24, 0x8d2a4c8a);   /* 32 */

    /*- MD5  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, 0x6d9d6122U);   /* 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, 0x4bdecfa9U);    /* 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, 0x289b7ec6U);   /* 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, 0x4881d05U);     /* 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, 0x1fa27cf8U);   /* 47 */

    HH(b, c, d, a, x[2], S34, 0xc4ac5665);    /* 48 */

    /*- MD5  Round 4 */
    II(a, b, c, d, x[0], S41, 0xf4292244);    /* 49 */

    II(d, a, b, c, x[7], S42, 0x432aff97U);    /* 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, 0x655b59c3U);   /* 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, 0x6fa87e4fU);    /* 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, 0x4e0811a1U);   /* 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, 0x2ad7d2bbU);    /* 63 */

    II(b, c, d, a, x[9], S44, 0xeb86d391);    /* 64 */

    /*- 更新状态字 */
    state[0] += a;

    state[1] += b;

    state[2] += c;

    state[3] += d;

    /*CM_Memset((void *)x, (CM_UINT8)0u, sizeof(x)); 信息安全措施，可不处理*/
}

/**
* @brief MD5初始化
* @param context Context结构体指针
* @return 函数执行结果
* - ENUM_CM_TRUE 成功
* - ENUM_CM_FALSE 失败
* @details
* MD5 initialization. Begins an MD5 operation, writing a new context.
 */
ENUM_CM_RETURN CM_MD5_Init(INOUT MD5Context_T* context)
{
    /*- 变量初始化 */
    ENUM_CM_RETURN ret = ENUM_CM_FALSE;
    /*- 输入是否合法 */
    if (NULL != context)
    {
        /*- 初始化context结构体 */
        context->count[0] = context->count[1] = 0U;
        /* Load magic initialization constants.*/
        context->state[0] = 0x67452301U;
        context->state[1] = 0xEFCDAB89U;
        context->state[2] = 0x98BADCFEU;
        context->state[3] = 0x10325476U;
        ret = ENUM_CM_TRUE;
    }
    else
	{
		/*- 无处理 */
	}
    /*- 返回执行结果 */
    return ret;
}

/**
* @brief MD5更新操作
* @param context Context结构体指针
* @param buf 输入字符首指针
* @param len 输入字符数
* @return 函数执行结果
* - ENUM_CM_TRUE 成功
* - ENUM_CM_FALSE 失败
* @details
* MD5 block update operation. Continues an MD5 message-digest
* operation, processing another message block, and updating the
* context.
*/
ENUM_CM_RETURN CM_MD5_Update(INOUT MD5Context_T* context, IN const CM_UINT8 buf[], IN CM_UINT32 len)
{
    /*- 变量初始化 */
    ENUM_CM_RETURN rt = ENUM_CM_FALSE;
    CM_UINT32 i = 0U;
    CM_UINT32 index = 0U;
    CM_UINT32 partLen = 0U;

    /*- 输入是否合法 */
    if ((NULL != context) && (NULL != buf))
    {

        /*- 计算长度对64的余数 */
        index = (CM_UINT32)((context->count[0] >> 3U) & 0x3FU);

        context->count[0] += (CM_UINT32)(len << 3U);
        /*- @alias 计算位数 */
        if ((CM_UINT32)(context->count[0]) < (len << (CM_UINT32)3U))
        {
            context->count[1]++;
        }
        else
		{
			/*- 无处理 */
		}

        context->count[1] += (CM_UINT32)(len >> 29U);

        /*- 计算尾数 */
        partLen = 64U - index;

        /*- 长度是否够长，需要Transfrom运算 */
        if (len >= partLen)
        {
            /*- 首块的Transfrom运算 */
            (void)CM_Memcpy(((void*)(&context->buffer[index])), (size_t)partLen, (const void*)buf, (size_t)partLen);

            s_MD5_Transform(context->state, (const CM_UINT8*)context->buffer);
            /*- @alias 后续块的Transform运算 */
            for (i = partLen; (i + (CM_UINT32)63U) < (CM_UINT32)len; i += 64U)
            {
                s_MD5_Transform(context->state, &buf[i]);
            }

            /*- 长度余数置零 */
            index = (CM_UINT32)0u;
        }
        else
        {
            /*- 用所有数据更新Context */
            i = (CM_UINT32)0u;
        }

        /*- 更新Context,缓存遗留数据 */
        (void)CM_Memcpy(((void*)(&context->buffer[index])), (size_t)CM_UNSIGNED_INT_EXP(len - i), ((const void*)(&buf[i])), (size_t)CM_UNSIGNED_INT_EXP(len - i));
        rt = ENUM_CM_TRUE;
    }
    else
	{
		/*- 无处理 */
	}
    /*- 返回执行结果 */
    return rt;
}

/**
* @brief 确定最终的MD5数值
* @param context Context结构体指针
* @param digest MD5数值
* @return 函数执行结果
* - ENUM_CM_TRUE 成功
* - ENUM_CM_FALSE 失败
* @details
* MD5 finalization. Ends an MD5 message-digest operation, writing the
* the message digest and zeroizing the context.
*/
ENUM_CM_RETURN CM_MD5_Final(INOUT MD5Context_T* context, OUT CM_UINT8 digest[CM_MD5_SIZE_BYTE])
{
    /*- 变量初始化 */
    ENUM_CM_RETURN rt = ENUM_CM_FALSE;
    CM_UINT8 bits[8] = { 0u };
    CM_UINT32 index = 0U;
    CM_UINT32 padLen = 0U;

    /*- 输入是否合法 */
    if ((NULL != context) && (NULL != digest))
    {
        /*- 保存位数 */
        /* Save number of bits */
        s_MD5_Encode(bits, (CM_UINT32*)(context->count), (CM_UINT32)8u);
        
        /* Pad out to 56 mod 64.*/
        index = (CM_UINT32)((context->count[0] >> 3U) & 0x3fU);

        /*- @alias 确定尾数 */
        if (index < (CM_UINT32)56u)
        {
            padLen = 56U - index;
        }
        else
        {
            padLen = 120U - index;
        }

        /*- 更新Context */
        (void)CM_MD5_Update(context, s_PADDING, padLen);
        /* Append length (before padding) */
        (void)CM_MD5_Update(context, bits, (CM_UINT32)8u);

        /*- 计算输出结果 */
        /* Store state in digest */
        s_MD5_Encode(digest, (CM_UINT32*)(context->state), (CM_UINT32)16u);

        /*- 清理Context */
        /* Zeroize sensitive information.*/
        rt = CM_Memset((void*)context, (CM_UINT8)0u, sizeof(*context), sizeof(*context));
    }
    else
	{
		/*- 无处理 */
	}
    /*- 返回执行结果 */
    return rt;
}

/**
* @brief MD5计算
* @param inputString 输入字符串指针
* @param len 输入字符串长度
* @param pOutputMD5Buf MD5值
* @return 函数执行结果
* - ENUM_CM_TRUE 成功
* - ENUM_CM_FALSE 失败
*/
ENUM_CM_RETURN CM_MD5_Calc(IN const CM_UINT8* inputString, IN CM_UINT32 len, OUT CM_UINT8 pOutMD5Buf[CM_MD5_SIZE_BYTE])
{
    ENUM_CM_RETURN rt = ENUM_CM_FALSE;
    ENUM_CM_RETURN ret = ENUM_CM_FALSE;
    MD5Context_T context;

    /*- 输入是否合法 */
    if ((NULL != pOutMD5Buf) && (NULL != inputString))
    {
        /*- 初始化Context */
    	ret = CM_Memset((void*) &context, (CM_UINT8)0u, sizeof(MD5Context_T), sizeof(MD5Context_T));
    	ret = CM_Memset((void*)pOutMD5Buf, (CM_UINT8)0u, (size_t)CM_MD5_SIZE_BYTE, (size_t)CM_MD5_SIZE_BYTE);

        rt = CM_MD5_Init(&context);
        /*- 初始化Context是否成功 */
        if ((ENUM_CM_TRUE == rt) && (ENUM_CM_TRUE == ret))
        {
            /*- 用输入数据更新Context */
            rt = CM_MD5_Update(&context, inputString, len);
            /*- Context是否更新成功 */
            if (ENUM_CM_TRUE == rt)
            {
                /*- 计算最终的MD5值 */
                rt = CM_MD5_Final(&context, pOutMD5Buf);
            }
            else
            {
            	/*- 无处理 */
            }
        }
        else
		{
        	rt = ENUM_CM_FALSE;
		}
    }
    else
	{
		/*- 无处理 */
	}
    /*- 返回执行结果 */
    return rt;
}
