文章目录
- Abstract
- 1 Introduction
- 2 Specification of AES-CMAC
- 2.1 Basic Definitions
- 2.2 Overview
- 2.3 Subkey Generation Algorithm
- 2.4 MAC Generation Algorithm
- 2.5 MAC Verification Algorithm
- 3 Security Considerations
- 4 Test Vectors
- 5 测试代码
- 5.1 C语言版本
- 5.2 Python语言版本
- 6 SM4-CMAC
- 6.1 SM4算法库
- 6.2 SM4算法测试向量
- 6.3 SM4-CMAC算法测试向量
- 参考资料
Abstract
This memo specifies an authentication algorithm based on CMAC with the 128-bit Advanced Encryption Standard (AES). This new authentication algorithm is named AES-CMAC.
1 Introduction
AES-CMAC可以检测有意图的,未鉴权的数据修改,也包括意外修改。
AES比hash函数更容易使用。
2 Specification of AES-CMAC
2.1 Basic Definitions
见原文。
2.2 Overview
如果Message的size是128bit的整数倍,最后一个block在AES_K运算之前要和K1进行XOR运算;否则,最后一个block会使用10^i填充到128bit,并且在AES_K运算之前和K2进行XOR运算。
AES_K:使用秘钥K的AES-128。
M:message,被切分成了M_1,M_2,……, M_n, M_i就表示第i个block。M_1到M_n-1的长度都是128bits。M_n 的长度是≤128bits。
K1和K2是子密钥生成算法生成的两个子密钥。
2.3 Subkey Generation Algorithm
子密钥生成算法的输入输出表示:(K1,K2) := Generate_Subkey(K),K就是AES-128的秘钥,K1和K2就是算法输出结果,两个子密钥。
2.4 MAC Generation Algorithm
MAC生成算法被表示为:T := AES-CMAC(K,M,len),K表示秘钥,M就是要保护的message,len是消息长度(单位为octets)。校验MAC可以确定消息的完整性(integrity)和鉴权(authenticity)。
n就是block个数, flag就表示是否被整除了,true表示正好是128的整数倍,false表示反之。
2.5 MAC Verification Algorithm
3 Security Considerations
由AES算法提供安全性保证。 AES或者其他加密算法的优势部分在于秘钥K和实现的正确性。密钥的生成方式应满足 RFC 4086的伪随机性要求,并应安全保存。 当且仅当正确使用 AES-CMAC 时,它才能提供满足当前消息身份验证最佳实践的身份验证和完整性。
4 Test Vectors
见原文:https://datatracker.ietf.org/doc/html/rfc4493
5 测试代码
5.1 C语言版本
见原文。
5.2 Python语言版本
from .abscipheralgo import AbsCipherAlgo
class CmacAlgo:
byte_limit = 16
bit_limit = 128
const_rb = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x87')
const_zero = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
def xor_bit_limit(self, a:bytearray, b:bytearray) -> bytearray:
assert len(a) == self.byte_limit
assert len(b) == self.byte_limit
out = bytearray(self.byte_limit)
for i in range(self.byte_limit):
out[i] = a[i] ^ b[i]
return out
def left_shift_one_bit(self, input:bytearray) -> bytearray:
assert len(input) == self.byte_limit
overflow = bytearray(1)
output = bytearray(self.byte_limit)
for i in reversed(range(self.byte_limit)):
# make sure is a byte
output[i] = (input[i] << 1) & 0xFF
output[i] |= overflow[0]
overflow[0] = 1 if (input[i] & 0x80) else 0
return output
def generate_subkey(self, key: bytearray, cipher_algo:AbsCipherAlgo):
L = bytearray(self.byte_limit)
Z = bytearray(self.byte_limit)
tmp = bytearray(self.byte_limit)
for i in range(self.byte_limit):
Z[i] = 0
L = cipher_algo.encrypt(key, Z)
# If MSB(L) = 0, then K1 = L << 1
if (L[0] & 0x80) == 0:
K1 = self.left_shift_one_bit(L)
else:
tmp = self.left_shift_one_bit(L)
K1 = self.xor_bit_limit(tmp, self.const_rb)
if K1[0] & 0x80 == 0:
K2 = self.left_shift_one_bit(K1)
else:
tmp = self.left_shift_one_bit(K1)
K2 = self.xor_bit_limit(tmp, self.const_rb)
return K1,K2
"""
将最后一个block以10^i的方式补齐
"""
def padding(self, last_block: bytearray):
assert last_block is not None
pad = bytearray(self.byte_limit)
for j in range(self.byte_limit):
if j < len(last_block):
pad[j] = last_block[j]
elif j == len(last_block):
pad[j] = 0x80
else:
pad[j] = 0x00
return pad
def cmac(self, key: bytearray, input:bytearray, cipher_algo:AbsCipherAlgo) -> bytes:
K1, K2 = self.generate_subkey(key, cipher_algo)
# n is number of rounds
# c语言直接除,因为int截断
n = (len(input) + self.byte_limit - 1) // self.byte_limit
if n == 0:
n = 1
# last block is not a complete block
flag = 0
else:
# last block is a complete block
if len(input) % self.byte_limit == 0:
flag = 1
else:
flag = 0
# 计算M_last
# last block is complete
if flag:
M_last = self.xor_bit_limit(input[(n - 1) * self.byte_limit : ], K1)
else:
padded = self.padding(input[(n - 1) * self.byte_limit : ])
M_last = self.xor_bit_limit(padded, K2)
X = bytearray(self.byte_limit)
for i in range(self.byte_limit):
X[i] = 0
for i in range(n-1):
Y = self.xor_bit_limit(X, input[self.byte_limit * i : self.byte_limit * (i + 1)])
X = cipher_algo.encrypt(key, Y)
Y = self.xor_bit_limit(X, M_last)
X = cipher_algo.encrypt(key, Y)
mac = bytearray(self.byte_limit)
for i in range(self.byte_limit):
mac[i] = X[i]
return mac
from .cmacalgo import CmacAlgo
class Cmac128Algo(CmacAlgo):
const_rb = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x87')
const_zero = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
def __init__(self):
self.byte_limit = 16
self.bit_limit = 128
self.const_rb = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x87')
self.const_zero = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
from .cmacalgo import CmacAlgo
class Cmac256Algo(CmacAlgo):
const_rb = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x87')
const_zero = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
def __init__(self):
self.byte_limit = 32
self.bit_limit = 256
const_rb = bytearray(
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x87')
const_zero = bytearray(
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
6 SM4-CMAC
6.1 SM4算法库
Cryptography:https://blog.51cto.com/u_16175518/6765321
CryptSM4:https://cloud.tencent.com/developer/article/1751336?areaId=106001
https://github.com/yang3yen/pysm4
算法padding模式:PKCS7,None
6.2 SM4算法测试向量
明文:
0x40, 0x10, 0xFF, 0xEF, 0x00, 0x45, 0x93, 0x00, 0xAB, 0xCD, 0xEE, 0x00, 0xF0, 0xF4, 0x00, 0x88,
0xA2, 0x19, 0x09, 0x67, 0x87, 0x0C, 0xFF, 0x54, 0xF3, 0x12, 0x50, 0x33, 0xC7, 0xF9, 0x1F, 0xDF,
0x13, 0x73, 0x4F, 0x67, 0xD7, 0xEB, 0xCC, 0x44, 0xF1, 0x12, 0x90, 0x18, 0xA7, 0xA9, 0x1F, 0x03,
0x13, 0x88, 0x4F, 0x67, 0xD7, 0xEB, 0xCC, 0x44, 0xF1, 0x12, 0x46, 0x18, 0x64, 0xA9, 0x1F, 0xEE,
密钥:
0x23, 0x33, 0x41, 0x67, 0x87, 0xEB, 0xFF, 0x54, 0xF1, 0x12, 0x90, 0x11, 0xC7, 0xF9, 0x1F, 0x23
ECB密文:
0x92, 0x31, 0xc8, 0x55, 0xcb, 0x47, 0x0b, 0x07, 0x93, 0x1d, 0xff, 0xb8, 0x14, 0x23, 0x4c, 0x63,
0x06, 0xc9, 0x57, 0x3f, 0x37, 0x12, 0x3b, 0x54, 0xef, 0x61, 0xb6, 0x82, 0x18, 0x83, 0x1f, 0xa2,
0x8a, 0xb6, 0xa9, 0xcc, 0x02, 0xa7, 0xd3, 0x26, 0xeb, 0xeb, 0xe4, 0x5a, 0x48, 0xab, 0xb2, 0xe1,
0x5b, 0x38, 0x67, 0xe9, 0xb4, 0x16, 0x47, 0x6a, 0x0a, 0xf3, 0x9b, 0xef, 0x12, 0xb0, 0x28, 0xb5,
CBC密文:
0x0f, 0xa7, 0x44, 0xda, 0xba, 0x7c, 0x9d, 0xe3, 0xc3, 0xde, 0xab, 0xe7, 0x42, 0x4f, 0x29, 0x68,
0xa2, 0xdd, 0x8e, 0x21, 0x60, 0xd5, 0xbd, 0x5f, 0xf1, 0x63, 0x7e, 0xa7, 0xb2, 0xdf, 0x3f, 0x89,
0x73, 0xc9, 0x72, 0xe6, 0x69, 0x2d, 0xee, 0x23, 0xe5, 0x10, 0xd7, 0x57, 0x7d, 0xc2, 0x1f, 0x0b,
0x6f, 0x12, 0x51, 0x6d, 0xc3, 0x28, 0x2b, 0x07, 0xee, 0xd2, 0x56, 0x05, 0x22, 0x1e, 0x3d, 0xad,
6.3 SM4-CMAC算法测试向量
明文:
0x40, 0x10, 0xFF, 0xEF, 0x00, 0x45, 0x93, 0x00, 0xAB, 0xCD, 0xEE, 0x00, 0xF0, 0xF4, 0x00, 0x88,
0xA2, 0x19, 0x09, 0x67, 0x87, 0x0C, 0xFF, 0x54, 0xF3, 0x12, 0x50, 0x33, 0xC7, 0xF9, 0x1F, 0xDF,
0x13, 0x73, 0x4F, 0x67, 0xD7, 0xEB, 0xCC, 0x44, 0xF1, 0x12, 0x90, 0x18, 0xA7, 0xA9, 0x1F, 0x03,
0x13, 0x88, 0x4F, 0x67, 0xD7, 0xEB, 0xCC, 0x44, 0xF1, 0x12, 0x46, 0x18, 0x64, 0xA9, 0x1F, 0xEE,
密钥:
0x23, 0x33, 0x41, 0x67, 0x87, 0xEB, 0xFF, 0x54, 0xF1, 0x12, 0x90, 0x11, 0xC7, 0xF9, 0x1F, 0x23
CMAC密文:
0xb0, 0xeb, 0x38, 0x61, 0xe6, 0xc5, 0xc1, 0x09, 0x89, 0x61, 0x30, 0x7d, 0x49, 0xb1, 0x7a, 0x7d
参考资料
[1] https://datatracker.ietf.org/doc/html/rfc4493
[2]