AES-GCM算法简介
AES是一种对称加密算法,GCM是对该对称加密采用Counter模式,并带有GMAC消息认证码。
AES-GCM算法是带认证和加密的算法,同时可以对给定的原文,生成加密数据和认证码。参数如下:
1)带加密的原文、2)存储加密后密文、3)IV向量、4)生成的消息验证码tag、5)额外的消息认证数据aad,通信双方需要共享。
Ek | 使用密钥k对输入做对称加密运算 |
---|---|
XOR | 异或运算 |
Mh | 将输入与密钥h在有限域GF(2^128)上做乘法 |
分组密码模式
-
ECB(Electronic Codebook)电子密码本模式
将明文进行分组,分组长度可为128,256,或512bits。采用ECB模式的分组密码算法加密过程如下图:
-
CBC(cipher block chaining)密码分组链接模式
每一组明文在加密前都与前面的密文分组进行异或操作,由于第一个明文分组前面没有密文分组所以需要准备一个与密文分组长度相同的比特序列来代替密文分组,这个比特序列被称作初始化向量IV。
-
PKCS7填充方案
ECB、CBC模式中需要进行填充,常用的是PKCS7填充方案,以AES-CBC为例,分组长度16字节,若明文28字节,则需要在明文末尾填充4字节的04,若待明文明文长度是16字节,则需要额外填充16字节的16,解密后取最后一个明文字节的值,比如是x,则要去掉尾部x字节后才是真正明文消息。
-
CTR ( CounTeR 计数器模式)
CTR模式使用与明文长度相同的计数值参与运算,通过加密计数值来产生密钥流,然后让密钥流与明文进行异或操作来加密明文。加入最后一个明文长度不是分组长度整数被,则对密钥流截取明文长度后异或加密,所以这种方式不需要对明文分组进行填充。(ECB、CBC需要填充)
计数器模式下,每次与明文分组进行XOR的比特序列是不同的,因此,计数器模式解决了ECB模式中,相同的明文会得到相同的密文的问题。CBC,CFB,OFB模式都能解决这个问题,但CTR的另两个优点是:1)支持加解密并行计算,可事先进行加解密准备;2)错误密文中的对应比特只会影响明文中的对应比特等优点。
但CTR仍然不能提供密文消息完整性校验的功能。 -
MAC ( Message Authentication Code, 消息验证码)
想要校验消息的完整性,必须引入另一个概念:消息验证码。消息验证码是一种与秘钥相关的单项散列函数。
密文的收发双发需要提前共享一个秘钥。密文发送者将密文的MAC值随密文一起发送,密文接收者通过共享秘钥计算收到密文的MAC值,这样就可以对收到的密文做完整性校验。当篡改者篡改密文后,没有共享秘钥,就无法计算出篡改后的密文的MAC值。
如果生成密文的加密模式是CTR,或者是其他有初始IV的加密模式,别忘了将初始的计时器或初始向量的值作为附加消息与密文一起计算MAC。 -
GMAC ( Galois message authentication code mode, 伽罗瓦消息验证码 )
对应到上图中的消息认证码,GMAC就是利用伽罗华域(Galois Field,GF,有限域)乘法运算来计算消息的MAC值。假设秘钥长度为128bits, 当密文大于128bits时,需要将密文按128bits进行分组。应用流程如下图:
-
GCM( Galois/Counter Mode )
GCM中的G就是指GMAC,C就是指CTR。
GCM可以提供对消息的加密和完整性校验,另外,它还可以提供附加消息的完整性校验。在实际应用场景中,有些信息是我们不需要保密,但信息的接收者需要确认它的真实性的,例如源IP,源端口,目的IP,IV,等等。因此,我们可以将这一部分作为附加消息加入到MAC值的计算当中。下图的Ek表示用对称秘钥k对输入做AES运算。最后,密文接收者会收到密文、IV(计数器CTR的初始值)、MAC值。
AES-128-GCM的例子
#define MBEDTLS_CONFIG_H
/* System support */
#define MBEDTLS_HAVE_ASM
/* mbed TLS modules */
#define MBEDTLS_AES_C
#define MBEDTLS_GCM_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_MD_C
#define MBEDTLS_OID_C
#define MBEDTLS_CIPHER_C
#define MBEDTLS_PKCS5_C
/* Save RAM at the expense of ROM */
#define MBEDTLS_AES_ROM_TABLES
#include "link_key.h"
#include <stdio.h>
#include <string.h>
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/platform.h"
#include "mbedtls/md.h"
#include "mbedtls/pkcs5.h"
#include "mbedtls/gcm.h"
#include "mbedtls/pk.h"
#include "mbedtls/timing.h"
#define LINK_KEY_RANDOM_LEN 16
#define IV_LEN 12
#define TAG_LEN 16
#define ADD_LEN 4
uint8_t keyFactor1[LINK_KEY_RANDOM_LEN];
uint8_t keyFactor2[LINK_KEY_RANDOM_LEN];
uint8_t rootKey[LINK_KEY_RANDOM_LEN];
uint8_t tagBuf[TAG_LEN];
uint8_t IV[IV_LEN];
static const uint8_t addition[ADD_LEN] = {0xfe, 0xa5, 0x5a, 0xef};
static void dump_buf(char *info, uint8_t *buf, uint32_t len)
{
mbedtls_printf("%s", info);
for (int i = 0; i < len; i++) {
mbedtls_printf("%s%02X%s", i % 16 == 0 ? "\n ":" ",
buf[i], i == len - 1 ? "\n":"");
}
}
static int timer_entropy_poll(void *data, uint8_t *output, size_t len, size_t *olen)
{
unsigned long timer = mbedtls_timing_hardclock();
((void) data);
*olen = 0;
if( len < sizeof(unsigned long) )
return( 0 );
memcpy( output, &timer, sizeof(unsigned long) );
*olen = sizeof(unsigned long);
return( 0 );
}
static int GenRandom(const uint8_t *custom, uint8_t * out, size_t out_len)
{
int ret = 0;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_add_source(&entropy, timer_entropy_poll, NULL,
MBEDTLS_ENTROPY_MAX_GATHER,
MBEDTLS_ENTROPY_SOURCE_STRONG);
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, custom, strlen(custom));
if (ret != 0)
goto cleanup;
ret = mbedtls_ctr_drbg_random(&ctr_drbg, out, out_len);
cleanup:
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
return ret;
}
int GenKeyComponent(void)
{
int ret;
const uint8_t *factor1 = "keyFactor1";
const uint8_t *factor2 = "keyFactor2";
ret = GenRandom(factor1, keyFactor1, LINK_KEY_RANDOM_LEN);
if (ret != 0)
goto exit;
ret = GenRandom(factor2, keyFactor2, LINK_KEY_RANDOM_LEN);
if (ret != 0)
goto exit;
exit:
return ret;
}
void GenRootAesKey(void)
{
mbedtls_md_context_t md_ctx;
const mbedtls_md_info_t *info_sha256;
const uint8_t *randomkey = "rootkey";
uint8_t password[LINK_KEY_RANDOM_LEN] = {0};
uint8_t salt[LINK_KEY_RANDOM_LEN];
mbedtls_md_init(&md_ctx);
info_sha256 = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
if( info_sha256 == NULL )
{
return;
}
if(mbedtls_md_setup( &md_ctx, info_sha256, 1 )!= 0)
{
return;
}
GenRandom(randomkey, password, LINK_KEY_RANDOM_LEN);
for (int i = 0; i < LINK_KEY_RANDOM_LEN; i++)
{
salt[i] = keyFactor1[i]^keyFactor2[i];
}
mbedtls_pkcs5_pbkdf2_hmac(&md_ctx, salt, LINK_KEY_RANDOM_LEN, salt, LINK_KEY_RANDOM_LEN, 1, LINK_KEY_RANDOM_LEN, rootKey);
mbedtls_md_free(&md_ctx);
}
void AesGcmEncryptFunction(uint8_t *out, const uint8_t *in, uint32_t len)
{
mbedtls_gcm_context ctx;
mbedtls_cipher_id_t cipher = MBEDTLS_CIPHER_ID_AES;
uint32_t keybit = LINK_KEY_RANDOM_LEN * 8;
mbedtls_gcm_init(&ctx);
mbedtls_gcm_setkey(&ctx, cipher, rootKey, keybit);
GenRandom("mbedtls_IV", IV, IV_LEN);
mbedtls_gcm_crypt_and_tag(&ctx, MBEDTLS_GCM_ENCRYPT, len, IV, IV_LEN, addition, ADD_LEN, in, out, TAG_LEN, tagBuf);
mbedtls_gcm_free(&ctx);
}
void AesGcmDecryptFunction(uint8_t *out, const uint8_t *in, uint32_t len)
{
mbedtls_gcm_context ctx;
mbedtls_cipher_id_t cipher = MBEDTLS_CIPHER_ID_AES;
uint32_t keybit = LINK_KEY_RANDOM_LEN * 8;
mbedtls_gcm_init(&ctx);
mbedtls_gcm_setkey(&ctx, cipher, rootKey, keybit);
int ret = mbedtls_gcm_auth_decrypt(&ctx, len, IV, IV_LEN, addition, ADD_LEN, tagBuf, TAG_LEN, in, out);
mbedtls_gcm_free(&ctx);
}
int main(void)
{
uint8_t out[32];
uint8_t deout[32];
uint8_t rsaEn[512] = {0};
uint8_t rsaDe[32] = {0};
//uint8_t randomOut[16];
const uint8_t *buff = "CXF123CAIXUEFNEG4561lj398709f03caix3245-0i@(@()%*U13456454D5234@¥%%g";
int j = 0;
for (int i = 0; i < 10; i++)
{
uint8_t in[32];
memcpy(in, buff+2*j, 31);
GenKeyComponent();
GenRootAesKey();
AesGcmEncryptFunction(out, in, 31);
AesGcmDecryptFunction(deout, out, 31);
mbedtls_printf("%d:%s\n", i, deout);
}
return 0;
}