NTL加密---Rijndael:对称密码(可逆)(AES)(加密数据)

Rijndael原理

参考文献:《密码学C/C++语言实现》

:满足模p加法、乘法的结合律、交换律、分配律,且具有封闭性,且加法、乘法有单位元(存在对应的负数和倒数)。

有限域:有限个元素的域。

多项式计算:有限域  上计算,其有限域包含  个元素,每个元素都是一个多项式表示:  ,系数  为有限域  与  同构)中的元素。

举例:  :

多项式加法:由系数在  上的加法定义,假如  ,则f(x)+g(x)=2x^2+2x+1=1.因为  上1+1=0,因此采用系数表示法时可直接对系数相加。

多项式乘法:将第一个多项式中的每一项乘以第二个多项式中的每一项并将所有部分积求和而来。然后对这个和式对一个3阶即约多项式求余即可得到乘法运算的结果。

选择  作为即约多项式:

  该过程对应3元组的乘积(110)·(111)=(100)或者是所有表示的数值的乘积06·07=04.  上的加法和  的乘法构成交换群。同时满足域上的分配律。

不可约多项式:如果不存在多项式  使得  ,则f(x)为不可约多项式。

Rijndael256采用有限域  ,其即约多项式为  ,代表的元组为(100011011),对应的十六进制数字为‘011B’.

其携带一个特性:  乘以x可通过简单的系数异或得到。因为只要求mod m(x)的余数  .通过减m(x)求得。

     因此  上的两个多项式f和h的乘法运算可以通过使用如下算法加法:令g(x)为  上的一个生成多项式(g的幂可遍历其域中的所有元素),则存在m和n,使得  和  ,因此  .  在程序设计时可采用两个表,一个是g(x)的255个幂,一个是255个对数。在以上机制下,也可以在  上执行除法运算,  .

  运算高效实现:

AES、OTE等加密技术均用到了  运算。

Rijndael算法:对称分组加密算法,其分组大小和密钥长度可变。基于置换(数据重新排列)和代替(用一个单元数据替换另一个)实现

DES的加密结构:将分组划分为左右两半,将其中一部分进行轮转化,并将结果与剩下的一半进行异或。

Rijndael的加密结构:采用分层结构,连续的对整个分组应用不同的操作。

对一个分组的加密,循环的应用如下转换:

1)用第一个轮密钥与分组进行异或

2)执行L_r-1常规轮变换(一个常规轮变换包含以下四步骤:1.替换:使用S盒(非线性操作,提供了一个近似理想的抗差分攻击)替换分组的每个字节.2.置换:行位移变换(shiftRows)中置换分组的字节3.扩散:执行列混合(MixColumns)变换,4.轮密钥加:用当前的轮密钥与分组进行异或)

3)执行结束的轮变换,其中将常规轮变换中的MixColumns变换删去。

Rijndael轮密钥的实现:

加密和解密都需要产生L_r个轮密钥,合起来称为密钥表。密钥通过递归产生的4字节  .

密钥表最开始为用户秘密生成的  个字  .接下来的4字节  通过将前面的字  和  异或得到。

若  ,则在异或之前执行函数  :该函数由r(k)循环左移k字节、由Rijndael S盒替换S(r(k))以及与常数  进行异或等组成,因此  .

        常数c(j)可定义为  ,其中rc(j)是从  上递归决定的元素  .

对于长度为256位(即  )的密钥,需插入一个额外的S盒操作:若  ,则在异或操作之前用  替换  .

Rijndael替换盒(S盒):

其规定了一个分组中每一个字节在每一轮中如何替换为另一个值。其作用是将针对该算法的线性和差分密码分析与代数攻击的敏感性降到最低。

S盒包含一个256字节的列表,构造该列表时,先把每个非零字节当中  的元素。

S-盒是一个16*16的矩阵

static const u8 sbox[256] = {
   
               //0     1    2      3     4    5     6     7      8    9     A      B    C     D     E     F            0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,            0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,            0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,            0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,            0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,            0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,            0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,            0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,            0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,            0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,            0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,            0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,            0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,            0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,            0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,            0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };

逆S盒:解密时使用,通过对S盒使用仿射逆变换,然后在F_{2^8}上进行乘法逆运算。

static const u8 rsbox[256] = {
         0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
         0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
         0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
         0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
         0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
         0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
         0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
         0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
         0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
         0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
         0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
         0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
         0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
         0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
         0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
         0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };

Rijndael行移位变换:

在第i行,以字节为单位循环左移c_{L_b,i}个位置。

  
     inline void ShiftRows(block& state_)
      {
           uint8_t temp;
           auto& state = stateView(state_);

           // Rotate first row 1 columns to left  
           temp = state[0][1];
           state[0][1] = state[1][1];
           state[1][1] = state[2][1];
           state[2][1] = state[3][1];
           state[3][1] = temp;

           // Rotate second row 2 columns to left  
           temp = state[0][2];
           state[0][2] = state[2][2];
           state[2][2] = temp;

           temp = state[1][2];
           state[1][2] = state[3][2];
           state[3][2] = temp;

           // Rotate third row 3 columns to left
           temp = state[0][3];
           state[0][3] = state[3][3];
           state[3][3] = state[2][3];
           state[2][3] = state[1][3];
           state[1][3] = temp;
      }

Rijndael列混合变换:

Mixcolums的逆变换是将分组中的每一列(b_{i,j})乘以多项式r(x)=r_3 x^3 + r_2 x^2+r_1x+r_0.

  
    inline void MixColumns(block& state_)
      {
           auto& state = stateView(state_);
           uint8_t i;
           uint8_t Tmp, Tm, t;
           for (i = 0; i < 4; ++i)
          {
               t = state[i][0];
               Tmp = state[i][0] ^ state[i][1] ^ state[i][2] ^ state[i][3];
               Tm = state[i][0] ^ state[i][1]; Tm = xtime(Tm);  state[i][0] ^= Tm ^ Tmp;
               Tm = state[i][1] ^ state[i][2]; Tm = xtime(Tm);  state[i][1] ^= Tm ^ Tmp;
               Tm = state[i][2] ^ state[i][3]; Tm = xtime(Tm);  state[i][2] ^= Tm ^ Tmp;
               Tm = state[i][3] ^ t;           Tm = xtime(Tm);  state[i][3] ^= Tm ^ Tmp;
          }
      }

Rijndael轮密钥加:

轮的最后一步执行轮密钥与分组异或:(b)\leftarrow (b)\oplus (k).

Rijndaels代码实现:

//轮函数
       block AES<Portable>::roundEnc(block state, const block& roundKey)
      {
           SubBytes(state);//字段所有字节的S盒替换
           ShiftRows(state);//行位移变换
           MixColumns(state);//列混合变换
           state = state ^ roundKey;//与轮密钥相加
           return state;
      }
       block AES<Portable>::finalEnc(block state, const block& roundKey)
      {
           SubBytes(state);//字段所有字节的S盒替换
           ShiftRows(state);//行位移变换
           state = state ^ roundKey;//与轮密钥相加
           return state;
      }

3.Rijndael实现

参考代码:https://github.com/ladnir/cryptotools/tree/6b2ea3ba8c10e048da1608e149db25aeb179ccda

CryptoTools 是一个可移植的 c++14 库,其中包含一组用于构建加密协议的工具。这包括异步网络 (Boost Asio)、几个快速原语,例如 AES (AES-NI)、Blake2(组装)和椭圆曲线加密(Relic-Toolkit、Miracl 或 libsodium)。

Rijndael最大轮数设置:

static const int rijndael256_rounds = 14;

Rijndael最小单位为block类

SIMD指令集介绍

//参考文献:https://blog.csdn.net/jgj123321/article/details/95633431
/*
1.SIMD(Single Instruction Multiple Data):单个指令操作多个数据的运算技术,是CPU基本指令集的扩展,目前intel处理器支持的SIMD技术包含MMX,SSE,AVX.
SIMD的初衷:传统处理器虽然是32位or64位,但使用它们处理一个8bit的数据时,一个32位只能处理一个8bit数据。但SIMD是想将32位拆分为4
*/
/*
SSE:有属于自己的8个128位长的寄存器,每一个寄存器(32*4)可支持4个单精度浮点数同时计算。
封装函数库:
#include <xmmintrin.h>   // SSE
#include <emmintrin.h>   // SSE2
#include <pmmintrin.h>   // SSE3
#include <tmmintrin.h>   // SSSE3
#include <smmintrin.h>   // SSE4.1
#include <nmmintrin.h>   // SSE4.2
#include <wmmintrin.h>   // AES
SSE指令格式:
一般由三部分组成:
第一部分前缀_mm 表示该函数属于SSE指令集。  
第二部分指令操作类型,如_add、_mul等
第三部分由两个字母组成。第一个字母表示对结果变量的影响方式,为p或s。
p(packed:包裹指令) :该指令一次对四个浮点数(data0~data3)同时进行计算;
s(scalar:标量指令):该指令对寄存器中的第一个元素进行运算,即一次只对m寄存器中的data0进行计算;
第二个字母表示参与运算的数据类型,s表示32位浮点数,d表示64位浮点数,i32表示带符号32位整型,i64表示带符号64位整型,u32表示无符号32位整型。
常用SSE指令:
·load系列,用于加载数据(从内存到暂存器),大部分需要16字节对齐
·set系列,用于加载数据,类似于load操作,但是大部分需要多条指令完成,可能不需要16字节对齐
·store系列,将计算结果等SSE暂存器的数据保存到内存中,与load系列函数的功能对应,基本上都是一个反向的过程。
·算数指令系列,SSE提供了大量的浮点运算指令,包括加法、减法、乘法、除法、开方、最大值、最小值等等
·数据类型转换系列
*/

Block类介绍

/*字节对齐主要是提高内存的访问效率[读取内存数据是有规则,若不对齐可能影响内存的访问效率]
alignas关键字用来设置内存中对齐方式,最小是8字节对齐,也可以是16,32,64,128.计算方法就是对齐数的整数倍
举例
struct alignas(8) test2{
char c;
int i;
double d;
};*/

//8位对齐,若数据大小小于8字节,则内存占用8字节。若数据大小在8-16字节,则内存占用16字节。
struct alignas(16) block
  {
//是否支持SSE指令
#ifdef OC_ENABLE_SSE2
       __m128i mData;//若支持字节设置为m128i类型,类型值会被分为4部分且4部分的值相等。
//mData可看作一个整数类似一个数组,但却不能直接调用,需重新定义:
//int32_t *p = (int *) &nums;
//cout<<p[0]<<endl;
//这样才能取出里面的值。
#else
       std::uint64_t mData[2];//128位--16字节 sizeof(block)=16
#endif

       block() = default;
       block(const block&) = default;
       block(uint64_t x1, uint64_t x0)
      {
#ifdef OC_ENABLE_SSE2
           mData = _mm_set_epi64x(x1, x0);
#else
           as<uint64_t>()[0] = x0;
           as<uint64_t>()[1] = x1;
#endif
      };
//设置每个字节的值
   block(char e15, char e14, char e13, char e12, char e11, char e10, char e9, char e8, char e7, char e6, char e5, char e4, char e3, char e2, char e1, char e0){}
//引用重载 &
//操作符重载 | & << >> + - !=
   
//static_assert:由于做编译期间的静态断言 语法:static_assert(常量表达式,提示字符串) 常量表达式的值为false会返回错误位置所在行和提示字符串。
static_assert(sizeof(block) == 16, "expected block size");
static_assert(std::alignment_of<block>::value == 16, "expected block alignment");
static_assert(std::is_trivial<block>::value, "expected block trivial");
static_assert(std::is_standard_layout<block>::value, "expected block pod");    

//赋值
inline block toBlock(std::uint64_t high_u64, std::uint64_t low_u64)
  {
       block ret;
       ret.as<std::uint64_t>()[0] = low_u64;
       ret.as<std::uint64_t>()[1] = high_u64;
       return ret;
  }
inline block toBlock(std::uint64_t low_u64) { return toBlock(0, low_u64); }
inline block toBlock(const std::uint8_t* data) { return toBlock(((std::uint64_t*)data)[1], ((std::uint64_t*)data)[0]); }

//extern全局变量
extern const block ZeroBlock;
extern const block OneBlock;
extern const block AllOneBlock;
extern const block CCBlock;
extern const std::array<block, 2> zeroAndAllOne;
数据存储、参数传递等基础块采用Block256

//以32字节为单位的基础块
struct Block256: public std::array<block, 2>
  {
   private:
       using Base = std::array<block, 2>;//Base为保存2个block类型的固定数组(32字节)

   public:
       Block256() = default;
       using Base::Base;
       using Base::operator=;

       Block256(block b0, block b1) : Base({b0, b1}) {}
       Block256(const std::uint8_t* data) : Base({toBlock(data), toBlock(data + 16)}) {}
       Block256(std::uint64_t low_u64) : Base({toBlock(low_u64), toBlock((std::uint64_t) 0)}) {}

       const unsigned char* data() const
      {
           // Unsafe, but I don't see a better way.
           return (const unsigned char*) &(*this)[0];
      }

       unsigned char* data()
      {
           return (unsigned char*) &(*this)[0];
      }
  };

Rijndael256Enc:

 
class Rijndael256Enc
  {
   public:
       using Block = Block256; //最小块为32字节
       static const int rounds = details::rijndael256_rounds; //正常轮为14轮
       std::array<Block, rounds + 1> mRoundKey;//存储轮密钥的字段

       // Default constructor leaves the class in an invalid state
       // until setKey(...) is called.
  //默认构造函数
       Rijndael256Enc() = default;
       Rijndael256Enc(const Rijndael256Enc&) = default;

       // Constructor to initialize the class with the given key
       //初始化用户密钥的构造函数
       Rijndael256Enc(const Block& userKey)
      {
           setKey(userKey);
      }

       // Set the key to be used for encryption.
  //设置用户密钥
       void setKey(const Block& userKey);
//加密明文,并将结果存储为密文
       void encBlock(const Block& plaintext, Block& ciphertext) const
      {
           encBlocksFixed<1>(&plaintext, &ciphertext);
      }
//加密明文返回密文
       Block encBlock(const Block& plaintext) const
      {
           Block ciphertext;
           encBlock(plaintext, ciphertext);
           return ciphertext;
      }

       // Instantiated only for {1, 2, 3, 4} blocks.
       template<size_t blocks>
       void encBlocksFixed(const Block* plaintext, Block* ciphertext) const;
       template<size_t blocks>
       void encBlocksFixed(const Block (&plaintext)[blocks], Block (&ciphertext)[blocks]) const
      {
           encBlocksFixed(*plaintext[0], &ciphertext[0]);
      }

       void encBlocks(const Block* plaintexts, size_t blocks, Block* ciphertext) const;
//轮函数
       static Block roundEnc(Block state, const Block& roundKey);
       auto Rijndael256Enc::roundEnc(Block state, const Block& roundKey) -> Block
  {
       __m128i b0 = state[0];
       __m128i b1 = state[1];

       // Use the AES round function to implement the Rijndael256 round function.
       rotateRows256Undo128<true>(b0, b1);
       b0 = _mm_aesenc_si128(b0, roundKey[0]);
       b1 = _mm_aesenc_si128(b1, roundKey[1]);

       return {b0, b1};
  }
  //128位半字节之间交换字节相当于整体向左旋转,然后在每个半字节内向右旋转。解密方法相同但有反向移位
       static inline void rotateRows256Undo128(__m128i& b0, __m128i& b1) {
       __m128i mask;
       if (encrypt)
      {
           mask = _mm_setr_epi8(0, -1, -1, -1,
                                0,  0, -1, -1,
                                0,  0, -1, -1,
                                0,  0,  0, -1);
      }
       else
      {
           mask = _mm_setr_epi8(0,  0,  0, -1,
                                0,  0, -1, -1,
                                0,  0, -1, -1,
                                0, -1, -1, -1);
      }
       __m128i b0_blended = _mm_blendv_epi8(b0, b1, mask);
       __m128i b1_blended = _mm_blendv_epi8(b1, b0, mask);

       //128bit的AES旋转是不同的,在两半内旋转
       __m128i perm;
       if (encrypt)
      {
           perm = _mm_setr_epi8( 0,  1,  6,  7,
                                 4,  5, 10, 11,
                                 8,  9, 14, 15,
                                12, 13,  2,  3);
      }
       else
      {
           perm = _mm_setr_epi8( 0,  1, 14, 15,
                                 4,  5,  2,  3,
                                 8,  9,  6,  7,
                                12, 13, 10, 11);
      }
       b0 = _mm_shuffle_epi8(b0_blended, perm);
       b1 = _mm_shuffle_epi8(b1_blended, perm);
  }

   
       static Block finalEnc(Block state, const Block& roundKey);
       auto Rijndael256Enc::finalEnc(Block state, const Block& roundKey) -> Block
  {
       __m128i b0 = state[0];
       __m128i b1 = state[1];
       rotateRows256Undo128<true>(b0, b1);
       b0 = _mm_aesenclast_si128(b0, roundKey[0]);
       b1 = _mm_aesenclast_si128(b1, roundKey[1]);
       return {b0, b1};
  }
  };

解密

  
 auto Rijndael256Dec::roundDec(Block state, const Block& roundKey) -> Block
  {
       __m128i b0 = state[0];
       __m128i b1 = state[1];
       // Use the AES round function to implement the Rijndael256 round function.
       rotateRows256Undo128<false>(b0, b1);
       b0 = _mm_aesdec_si128(b0, roundKey[0]);
       b1 = _mm_aesdec_si128(b1, roundKey[1]);

       return {b0, b1};
  }

   auto Rijndael256Dec::finalDec(Block state, const Block& roundKey) -> Block
  {
       __m128i b0 = state[0];
       __m128i b1 = state[1];
       rotateRows256Undo128<false>(b0, b1);
       b0 = _mm_aesdeclast_si128(b0, roundKey[0]);
       b1 = _mm_aesdeclast_si128(b1, roundKey[1]);

       return {b0, b1};
  }

猜你喜欢

转载自blog.csdn.net/qq_38798147/article/details/127478810
今日推荐