AES128-CMAC

CMAC

目录

简介

CMAC的工作原理

CMAC示例

简介

CMAC(Cipher-based MAC),基于密码的MAC,是一种基于密码的MAC算法,它基于块密码算法(如AES)和一个密钥来生成认证码。

CMAC是一种对称密钥加密算法,通常与对称密钥算法(如AES)结合使用,以提供消息的完整性和真实性验证

本文主要用于安全算法验证(基于AES),故有些名词可能不太准确,具体算法可参考 https://datatracker.ietf.org/doc/html/rfc4493 .

(以上来自维基百科: CMAC1或 CMAC2)

CMAC的工作原理

初始化:CMAC使用一个固定长度的密钥来初始化。密钥的长度通常与底层的对称加密算法(如AES)相关联。

分块处理:首先,将消息分成多个固定长度的块。如果消息长度不是块大小的倍数,则可以使用填充来将其填充到合适的大小。

生成子密钥:**根据初始密钥生成用于加密的子密钥。通常,CMAC使用两个不同的子密钥,分别用于生成左右两个分支的子密钥。

生成MAC:

左分支:将消息的每个块与左分支的子密钥进行加密。对于最后一个块,如果长度不够,则使用填充。

右分支:将左分支的结果进行异或运算,然后再与右分支的子密钥进行加密。

结果:将右分支的加密结果截取指定长度作为最终的认证码。

认证:将消息的认证码与生成的MAC进行比较。如果两者相匹配,则消息未被篡改,认证成功。

代号(Char)

含义(Meaning)

b

加密块的位长(bit)

K

用于AES的密钥

K1

子密钥1,用于左分支

K2

子密钥2,用于右分支

M

消息

M_i

消息块i

CMAC示例

基于python的验证代码如下:

需要安装Crypto库 :

若提示Crypto库未找到,改下库文件夹名称:

代码如下

from Crypto.Cipher import AES

from Crypto.Hash import CMAC

from binascii import hexlify, unhexlify

key = unhexlify('0102030405060708090a0b0c0d0e0f10')

#print("key:", key.decode())

seed = unhexlify('100f0e0d0c0b0a090807060504030201')

#print("seed:", seed.decode())

mac = CMAC.new(key,seed,ciphermod=AES)

print("AES_CMAC:",mac.hexdigest())

# result : 5becb7b36a0c7e019e9caf10f3971b00

基于c/c++的验证代码如下:

#include "aes.h"

#include "windows.h"

#include

#include

#include

/*****************************************************************************

* @description : 这部分需要自己实现,采用AES-ECB的算法

*

* @param ( uint8_t ) *key

*

* @param ( uint8_t ) *input

*

* @param ( uint8_t ) *output

*

* @return ( none )

*****************************************************************************/

void AES_128(const uint8_t *key, const uint8_t *input, uint8_t *output)

{

// 实现 AES-128 加密,或者使用其他库

int a = 16;

aes_encrypt_ecb(key, 16, input, 16, output, &a);

}

/* For CMAC Calculation */

const unsigned char const_Rb[16] = {

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87};

const unsigned char const_Zero[16] = {

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

/*****************************************************************************

* @description : 计算XOR的结果

*

* @param ( unsigned char ) *a 需要计算的数据

*

* @param ( unsigned char ) *b 异或的对象

*

* @param ( unsigned char ) *out 输出结果

*

* @return ( none )

*****************************************************************************/

void xor_128(unsigned char *a, const unsigned char *b, unsigned char *out)

{

int i;

for (i = 0; i < 16; i++)

{

out[i] = a[i] ^ b[i];

}

}

void print_hex(char *str, unsigned char *buf, int len)

{

int i;

for (i = 0; i < len; i++)

{

if ((i % 16) == 0 && i != 0)

printf(str);

printf("%02x", buf[i]);

if ((i % 4) == 3)

printf(" ");

if ((i % 16) == 15)

printf("\n");

}

if ((i % 16) != 0)

printf("\n");

}

void print128(unsigned char *bytes)

{

int j;

for (j = 0; j < 16; j++)

{

printf("%02x", bytes[j]);

if ((j % 4) == 3)

printf(" ");

}

}

void leftshift_onebit(unsigned char *input, unsigned char *output)

{

int i;

unsigned char overflow = 0;

for (i = 15; i >= 0; i--)

{

output[i] = input[i] << 1;

output[i] |= overflow;

overflow = (input[i] & 0x80) ? 1 : 0;

}

return;

}

void MAC_GenSubKey(unsigned char *key, unsigned char *K1, unsigned char *K2)

{

unsigned char L[16] = {

0,

};

unsigned char Z[16] = {

0,

};

unsigned char tmp[16] = {

0,

};

int i;

for (i = 0; i < 16; i++)

Z[i] = 0;

AES_128(key, Z, L);

if ((L[0] & 0x80) == 0)

{ /* If MSB(L) = 0, then K1 = L << 1 */

leftshift_onebit(L, K1);

}

else

{ /* Else K1 = ( L << 1 ) (+) Rb */

leftshift_onebit(L, tmp);

xor_128(tmp, const_Rb, K1);

}

if ((K1[0] & 0x80) == 0)

{

leftshift_onebit(K1, K2);

}

else

{

leftshift_onebit(K1, tmp);

xor_128(tmp, const_Rb, K2);

}

printf("\nLeft:\n");

for (int j = 0; j < 15; j++)

{

printf("%02x", K1[j]);

}

printf("\nRight:\n");

for (int j = 0; j < 15; j++)

{

printf("%02x", K1[j]);

}

printf("\n");

}

void padding(unsigned char *lastb, unsigned char *pad, int length)

{

int j;

/* original last block */

for (j = 0; j < 16; j++)

{

if (j < length)

{

pad[j] = lastb[j];

}

else if (j == length)

{

pad[j] = 0x80;

}

else

{

pad[j] = 0x00;

}

}

}

void AES_CMAC(unsigned char *key, unsigned char *input, int length,

unsigned char *mac)

{

unsigned char X[16], Y[16], M_last[16], padded[16];

unsigned char K1[16], K2[16];

int n, i, flag;

MAC_GenSubKey(key, K1, K2);

n = (length + 15) / 16; /* n is number of rounds */

if (n == 0)

{

n = 1;

flag = 0;

}

else

{

if ((length % 16) == 0)

{ /* last block is a complete block */

flag = 1;

}

else

{ /* last block is not complete block */

flag = 0;

}

}

if (flag)

{ /* last block is complete block */

xor_128(&input[16 * (n - 1)], K1, M_last);

}

else

{

padding(&input[16 * (n - 1)], padded, length % 16);

xor_128(padded, K2, M_last);

}

for (i = 0; i < 16; i++)

X[i] = 0;

for (i = 0; i < n - 1; i++)

{

xor_128(X, &input[16 * i], Y); /* Y := Mi (+) X */

AES_128(key, Y, X); /* X := AES-128(KEY, Y); */

}

xor_128(X, M_last, Y);

AES_128(key, Y, X);

for (i = 0; i < 16; i++)

{

mac[i] = X[i];

}

}

/**

key : 100f0e0d0c0b0a090807060504030201

seed:0102030405060708090a0b0c0d0e0f10

result: 95c6652305da28e31d6a7ab99dfd2998

**/

int main()

{

unsigned char L[16], K1[16], K2[16], T[16], TT[12];

unsigned char M[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10};

unsigned char key[16] = {

0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};

printf("--------------------------------------------------\n");

printf("key:\n");

for (int i = 0; i < 15; i++)

{

printf("%02x", key[i]);

}

printf("\n");

MAC_GenSubKey(key, K1, K2);

printf("M ");

print_hex(" ", M, 16);

AES_CMAC(key, M, 16, T);

printf("AES_CMAC ");

print128(T);

printf("\n");

printf("--------------------------------------------------\n");

system("pause");

return 0;

}

示例数据

以下数据可供参考,用于验证算法准确性:

消息(Seed/Message)

密钥(Key)

结果(Result/Token)

100f0e0d0c0b0a090807060504030201

0102030405060708090a0b0c0d0e0f10

5becb7b36a0c7e019e9caf10f3971b00

0102030405060708090a0b0c0d0e0f10

100f0e0d0c0b0a090807060504030201

95c6652305da28e31d6a7ab99dfd2998

66B0CF31F56AC16ABF4610DF87A1AE20

3D2E6DE2A12517BAC5B31BBD0E7E3B54

0d4de052272cc4f56e2a4fbc8dcfa931

2025-05-03 12:14:22