密码库LibTomCrypt学习记录——(2.15)分组密码算法的工作模式——GCM加密认证模式

  1. GCM

GCM是一种有大吞吐能力的加密认证模式。其中主要适用了CRT模式和类似CBC模式的GHASH模式。CRT模式基本上没有大多变化,GHASH则是利用有限域上的乘法进行HASH,此运算可以通常预先计算和查表优化加速。

GCM两个基本模块 GHASH和GCTR。加解密和认证验证过程都离不开这两个模块。

参考文献

  1. NIST SP 800-38D
  2. The Galois Counter Mode of Operation (GCM)
  1. GCTR

GCTRK (ICB, X)的流程

准备:

  1. 128比特的分组**CIPH
  2. **K

输入:

  1. 初始计数块ICB;
  2. 任意长度的串X

输出:

  1. 串Y,长度为len(X).

步骤:

step 1.    如果X是空串,则返回空串Y。

step 2.    n = 向上取整( len(X) / 128 )

step 3.    按分组大小划分串X = X1 || X2 || ... || Xn-1 || Xn*

其中X1, X2,..., Xn-1是完整分块,而Xn*可能是完整分块也可能不是。

step 4.    CB1=ICB.

step 5.    For i = 2 to n, let CBi = inc32(CBi-1).

step 6.    For i = 1 to n-1, letYi = Xi⊕CIPHK(CBi).

step 7.    Yn* = Xn*⊕MBSlen(Xn*)( CIPHK(CBi) )

step 8.    Y = Y1 || Y2 || ... || Yn-1 || Yn*

step 9.    Return Y.

其中

  1. inc_32(x)就是:x的低32bit做 ++x mod 2^32;x的高bit不做变化

GCTR的流程图如下:

密码库LibTomCrypt学习记录——(2.15)分组密码算法的工作模式——GCM加密认证模式

GCM中GCTR的流程图

 

  1. GHASH

GHASHH (X)

准备:

  1. 块H

输入:

  1. 消息X,长度为分组长度的整数倍,即len(X) = 128m

输出:

  1. GHASHH (X).

步骤:

step 1.    将X按分组大小划分X = X1 || X2 || ... || Xm-1 || Xm. 每个块都是完整块。

step 2.    Y0 = 0128,即全零块.

step 3.    For i = 1 to m, Yi = (Yi-1 ⊕ Xi) • H.

step 4.    Return Ym.

其中

  1. 步骤3的•H表示有限域GF(2128)上的乘法。关于此128bit有限域的乘法详情以及快速优化参见以下相关文档。
    1. NIST SP 800-38D
    2. The Galois Counter Mode of Operation (GCM)

GHASH的流程图如下

密码库LibTomCrypt学习记录——(2.15)分组密码算法的工作模式——GCM加密认证模式

GCM中GHASH的流程图

  1. GCM认证加密方案

GCM-AEK (IV, P, A)

准备:

  1. 128比特分组密码 CIPH
  2. **K

输入:

  1. 初始化向量IV
  2. 明文P
  3. 关联数据(additional authenticated data) A

输出:

  1. 密文C
  2. 认证值 T(长度t)

步骤:

step 1.     H = CIPHK(0128).

step 2.    定义分块J0:

If len(IV)=96, J0 = IV || 031 ||1.

If len(IV)≠96, s = ( 128 - ( len(IV) mod 128 ) ) mod 128,

J0=GHASHH( IV || 0s+64 || [len(IV)]64 ).

step 3.    C=GCTRK(inc32(J0), P).

step 4.   u = ( 128 - ( len(C) mod 128 ) ) mod 128,v = ( 128 - ( len(A) mod 128 ) ) mod 128

step 5.    S = GHASHH (A || 0v || C || 0u || [len(A)]64 || [len(C)]64).

step 6.    T = MSBt ( GCTRK(J0, S) )

step 7.    Return (C, T).

 

GCM认证加密的流程图如下:

密码库LibTomCrypt学习记录——(2.15)分组密码算法的工作模式——GCM加密认证模式

GCM认证加密的流程图

  1. GCM验证解密方案

GCM-ADK (IV, C, A, T)

准备:

  1. 128比特分组密码 CIPH
  2. **K

输入

  1. 初始化向量IV
  2. 密文C
  3. 关联数据(additional authenticated data) A
  4. :认证值 T(长度t)

输出:

  1. 明文P (或者验证失败)

步骤:

 

step 1.    如果IV、A、C 的长度为不支持长度或或者len(T) ≠ t,则返回验证失败

step 2.    H = CIPHK(0128).

step 3.    定义分块J0:

If len(IV)=96, J0 = IV || 031 ||1.

If len(IV)≠96, s = ( 128 - ( len(IV) mod 128 ) ) mod 128,

J0=GHASHH( IV || 0s+64 || [len(IV)]64 ).  

step 4.    P=GCTRK(inc32(J0), C)

step 5.     u = ( 128 - (len(C) mod 128 ) ) mod 128,v = ( 128 - (len(A) mod 128 ) ) mod 128

step 6.    S = GHASHH (A || 0v || C || 0u || [len(A)]64 || [len(C)]64).

step 7.    T′ = MSBt ( GCTRK(J0, S) )

step 8.    如果 TT′=T, 返回P;否则返回失败。

GCM验证解密的流程图如下:

密码库LibTomCrypt学习记录——(2.15)分组密码算法的工作模式——GCM加密认证模式

GCM验证解密的流程图

  1. LibTomCrypt与GCM

 LibTomCrypt中与GCM相关的信息如下:

typedef struct {

   symmetric_key       K;

   unsigned char       H[16],        /* multiplier */

                       X[16],        /* accumulator */

                       Y[16],        /* counter */

                       Y_0[16],      /* initial counter */

                       buf[16];      /* buffer for stuff */

 

   int                 cipher,       /* which cipher */

                       ivmode,       /* Which mode is the IV in? */

                       mode,         /* mode the GCM code is in */

                       buflen;       /* length of data in buf */

 

   ulong64             totlen,       /* 64-bit counter used for IV and AAD */

                       pttotlen;     /* 64-bit counter for the PT */

#ifdef LTC_GCM_TABLES

   unsigned char       PC[16][256][16]  /* 16 tables of 8x128 */

#ifdef LTC_GCM_TABLES_SSE2

__attribute__ ((aligned (16)))

#endif

;

#endif 

} gcm_state;

 

GCM涉及的函数有

void gcm_mult_h(gcm_state *gcm, unsigned char *I);

int gcm_init(gcm_state *gcm, int cipher, const unsigned char *key, int keylen);

int gcm_reset(gcm_state *gcm);

int gcm_add_iv(gcm_state *gcm, const unsigned char *IV,     unsigned long IVlen);

int gcm_add_aad(gcm_state *gcm, const unsigned char *adata, unsigned long adatalen);

int gcm_process(gcm_state *gcm, unsigned char *pt, unsigned long ptlen, unsigned char *ct, int direction);

int gcm_done(gcm_state *gcm, unsigned char *tag, unsigned long *taglen);

int gcm_memory( int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *IV, unsigned long IVlen, const unsigned char *adata, unsigned long adatalen, unsigned char *pt, unsigned long ptlen, unsigned char *ct, unsigned char *tag,    unsigned long *taglen, int direction);

 

──────────────────────────────────────

void gcm_mult_h(gcm_state *gcm, unsigned char *I);

// [功能]   与H的乘法

  1. gcm                  // [输入/输出]GCM的状态
  2. I                  // [输入/输出]乘数,乘积 I = I*H

//备注:此为内部函数,不被外面调用

// 可以查8bit表加速运算

──────────────────────────────────────

 

通常的GCM工作流程如下

gcm_init( );

while(need_add_iv)

{

gcm_add_iv();//添加IV

}

while(need_add_aad)

{

gcm_add_aad();//添加AAD

}

while(need_add_pt)

{

gcm_process ();//不停的添加明文or密文

}

gcm_done( );//完成并获取TAG

 

──────────────────────────────────────

int gcm_init(gcm_state *gcm, int cipher, const unsigned char *key, int keylen);

// [功能]   初始化

  1. gcm                  // [输入/输出]GCM的状态
  2. cipher           // [输入]密码算法
  3. key                   // [输入]**
  4. keylen          // [输入]**长度

//备注:1. 生成扩展**

//       2. 建立16个快速运算表Mi[x], 0 <= i < 16, 0 <= x < 256

──────────────────────────────────────

 

──────────────────────────────────────

int gcm_reset(gcm_state *gcm);

// [功能]   重置GCM信息

gcm                  // [输入/输出]GCM的状态 

──────────────────────────────────────

 

──────────────────────────────────────

int gcm_add_iv(gcm_state *gcm, const unsigned char *IV, unsigned long IVlen)

// [功能]   添加IV

  1. gcm                  // [输入/输出]GCM的状态
  2. IV                // [输入]初始化向量
  3. IVlen            // [输入]初始化向量字节长度

//备注: 1 bit <= IV bit len <= 2^64 - 1 bit, 这里要求以字节为单位

// 由于IV长度可以很长,所以允许将较长的IV分多次添加,即可多次调用gcm_add_iv

──────────────────────────────────────

 

──────────────────────────────────────

int gcm_add_aad(gcm_state *gcm, const unsigned char *adata, unsigned long adatalen);

// [功能]   添加AAD(仅认证不加密的数据)

  1. gcm                  // [输入/输出]GCM的状态
  2. adata           // [输入]AAD
  3. adatalen            // [输入]AAD字节长度

//备注: 0 bit <= AAD bit len <= 2^64 - 1 bit, 这里要求以字节为单位

// 由于AAD长度可以很长,所以允许将较长的AAD分多次添加

// 第一次做gcm_add_aad时才能确认IV添加完毕,此时做添加IV的收尾工作

// 在添加AAD时就开始做与AAD相关的GHASH了

──────────────────────────────────────

 

──────────────────────────────────────

int gcm_process(gcm_state *gcm, unsigned char *pt, unsigned long ptlen, unsigned char *ct, int direction);

// [功能]   与H的乘法

  1. gcm                  // [输入/输出]GCM的状态
  2. pt                // [输入/输出]明文
  3. ptlen            // [输入]明文字节长度
  4. ct                // [输入/输出]密文
  5. direction            // [输入]加密还是解密

//备注:加密时,输入明文,输出密文;解密时,输入密文,输出明文。明密文长度一样

// 第一次做gcm_process时才知道添加aad完成,此时做添加AAD的收尾工作

// GCM的tag要等所有处理完毕,到gcm_done()去获得

──────────────────────────────────────

 

──────────────────────────────────────

int gcm_done(gcm_state *gcm, unsigned char *tag, unsigned long *taglen);

// [功能]   完成GCM并获得TAG

  1. gcm                  // [输入/输出]GCM的状态
  2. tag               // [输出]验证信息
  3. taglen          // [输入/输出]TAG字节长度

//备注:输入taglen,内部根据输入值返回正确的taglen

──────────────────────────────────────

 

──────────────────────────────────────

int gcm_memory( int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *IV, unsigned long IVlen, const unsigned char *adata, unsigned long adatalen, unsigned char *pt, unsigned long ptlen, unsigned char *ct, unsigned char *tag,    unsigned long *taglen, int direction);

// [功能]   与H的乘法

  1. cipher           // [输入]密码算法
  2. key                   // [输入]**
  3. keylen          // [输入]**长度
  4. IV                // [输入]初始化向量
  5. IVlen            // [输入]初始化向量字节长度
  6. adata           // [输入]AAD
  7. adatalen            // [输入]AAD字节长度
  8. pt                // [输入/输出]明文
  9. ptlen            // [输入]明文字节长度
  10. ct                // [输入/输出]密文
  11. direction            // [输入]加密还是解密
  12. tag               // [输出]验证信息
  13. taglen          // [输入/输出]TAG字节长度

//备注: 此函数只适合IV、AAD、明密文都不太长的情况

工作流程如下

gcm_init( );

gcm_add_iv();//添加IV

gcm_add_aad();//添加AAD

gcm_process ();//不停的添加明文or密文

gcm_done( );//完成并获取TAG

──────────────────────────────────────