初学者写加密算法必看

1. 如何循环左移?

1.1 先来区分一下概念:

  • 物理左移SHL:丢弃最高位,0补最低位
  • 循环左移ROTL:将丢弃的最高位补到低位

1.2 代码示例:32位无符号数左移和循环左移

#define SHL(x,n) (((x) & 0xFFFFFFFF) << n )
#define ROTL(x,n) (SHL((x),n) | ((x) >> (32 - n)))
  • a = 01111011 循环左移两位
  • b = a >> (8 - 2) = 00000001 记录即将被丢弃的高位
  • a = a << 2 物理左移
  • a = a | b = 01111011 | 00000001 = 11101101

1.3 解释代码(代码只针对无符号数)

  • 宏调用,在编译前把记号传递给程序,根据需要也可以改写成函数
  • SHL:左移,x为进行左移的数据,n为要左移的位数
  • ROTL:循环左移,x为进行左移的数据,n为要左移的位数,32为数据的位数

为什么要 & 0xFFFFFFFF 呢?

(这里扩展一下“&”与运算的用途)

总结:两位同时为1,结果才为1,否则结果为0。

用途:

  1. 清零(与全0按位与)
  2. 取一个数的指定位(取几位用几位1)
  3. 判断奇偶(看末位)

2. 如何大数转换呢?

2.1 先来看一下应用场景:

在加密算法中总需要将明文或者密钥进行分组

  • 例如,一般见到的input是这样的:
  •   uint8_t input[16] = {
          0x2b, 0x7e, 0x15, 0x16, 
          0x28, 0xae, 0xd2, 0xa6,
          0xab, 0xf7, 0x15, 0x88, 
          0x09, 0xcf, 0x4f, 0x3c,
      }
    
  • 需要将数据合成这样:
  •   uint8_t input_2[4] = {
          0x2b7e1516, 0x28aed2a6,0xabf71588, 0x09cf4f3c,
      }
    
  • 有时候又需要将上边的过程逆过来

2.2.1 代码示例1.0

    uint8_t Num_Array[MAXSIZE];
    void Group_out(uint16_t Num)
    {
        int i, j, x = sizeof(Num_Array) - 1;
        while(Num > 0)
        {
            i = 0;
            j = 0;
            i = Num % 16;
            Num = Num / 16;
            j = Num % 16;
            Num = Num % 16;
            Num_Array[x] = i + j * 16;
            x--;
        }
    }
  • 想法源自手算16进制的按权相加
  • 看着麻烦,于是有了2.0

2.2.2 代码示例2.0

    #define Group_out(n,b,i)                             \
    {                                                       \
        (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \
        (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \
        (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \
        (b)[(i) + 3] = (unsigned char) ( (n)       );       \
    }

    #define Group_in(n,b,i)                             \
    {                                                       \
        (n) = ( (unsigned long) (b)[(i)    ] << 24 )        \
            | ( (unsigned long) (b)[(i) + 1] << 16 )        \
            | ( (unsigned long) (b)[(i) + 2] <<  8 )        \
            | ( (unsigned long) (b)[(i) + 3]       );       \
    }

2.3 解释代码2.0

  • n 为一串数字,例如:0x12345678
  • b 为将一串数字分解后的数组,例如:0x12,0x34,0x56,0x78
  • i 为数组序号,因为需要分很多的数呀

原理:

发挥想象,通俗一点讲,0x12,0x34,0x56,0x78 <——> 0x12345678,无非就是将每个数放到合适的位置。

深究原理,其实与按权相加一样的道理:

  1. 左移,对于正数来说,左移相当于乘以2(但效率比乘法高);
  2. 右移,对于正数来说,右1移相当于除以2(但效率比除法高);

3. 加密数据填充(填充0x80、0x00、数据长度)

3.1 填充方式说明(SM3算法为例)

消息m的长度为l比特。首先将“1”添加到消息末尾,再添加k个“0”,k是满足 l + 1 + k ≡ 448mod512 的最小的非负整数。然后再添加一个64位比特串,该比特串是长度l的二进制表示。填充后的消息m’的比特长度为512的倍数。
padding

3.2.1 代码示例1.0

初接触时,按照填充描述,创建了一个巨长的数组,将每一比特填充了出来。代码巨长,时间复杂度和空间复杂度也巨大,现在看来真是一坨代码。这里不做展示了。

3.2.2 代码示例(部分)2.0

    const uint8_t 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
    }

    high = ( len[0] >> 29 ) | ( len[1] <<  3 );  // 数据长度(字节)高位
    low  = ( len[0] <<  3 );                    // 数据长度(字节)低位

    Group_out( high, msglen, 0 );               // 数据长度填入msglen数组中
    Group_out( low,  msglen, 4 );
 
    last = len[0] & 0x3F;                      // 取低六位数值
    padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); // 见下面讲解
    // 填充好的数据 = 原数据 + padding(截取padn) + msglen      

3.3 解释代码

先深入理解一下填充方式:

  • 首先,目的是将长度为l的消息填充为512比特的整数倍,那么用数学表达式的方法来看 (消息长度 + 填充的长度) mod 512 ≡ 0;
  • 接着,看一下填充的要求,“1”占了一个比特位,64比特的长度占了64个比特位,剩下的就是需要自己求的占k个比特位的“0”。由此就能知道 l + 1 + k ≡ 448mod512 的含义了,即 (消息长度 + 填充的长度 - 64) mod 512 ≡ 448
    • 当消息长度无限趋近于0,那 k + 1(填充的长度 - 64 )取最大值为448(64字节),所以设置padding初始数组为
    const uint8_t 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
    }
    • 还是由上边的数学表达式推出:k + 1 = 512*n + 448 -l (n = 0,1,2…)
    • 改成字节就是(padn为k+1的长度,last为消息长度,单位字节): padn = 64*n + 56 - last (n = 0,1,2…)
    • 可以由一个三目运算符表示出来:padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last )
    • 这样的话,在初始数组上截取我们需要的长度就可以了。
  • 最后,原数据 + padding(截取padn字节) + msglen 就得到了填充好的数据m’
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值