AES128-CMAC
目录
简介
- CMAC(Cipher-based MAC),基于密码的MAC,是一种基于密码的MAC算法,它基于块密码算法(如AES)和一个密钥来生成认证码。
- CMAC是一种对称密钥加密算法,通常与对称密钥算法(如AES)结合使用,以提供消息的完整性和真实性验证
- 本文主要用于安全算法验证(基于AES),故有些名词可能不太准确,具体算法可参考 https://datatracker.ietf.org/doc/html/rfc4493 .
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 <stdio.h> #include <stdint.h> #include <string.h> /***************************************************************************** * @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 |