C++实践(四):C++实现AES-CMAC算法-程序员宅基地

技术标签: C++  cmac  编程-C++  aes  

AES-CMAC

AES-CMAC使用了高级加密标准作为组分。为了产生一个消息认证码,CMAC需要一个密钥,消息message及消息的长度length作为输入,输出是消息认证码。
AES-CMAC的核心是CBC-MAC。对于待加密消息M,应用CBC-MAC算法。在CMAC操作中有两种情况:
如果输入消息长度等于Block的整数倍,最后的Block M_n需要先于K1异或再进行处理;
如果输入的消息长度不等于Block的整数倍,最后的Block M_n需要补齐到一个Block的大小,与K2异或,再进行处理。上一次处理的结果将成为下一次处理的输入。
这里写图片描述
子密钥生成算法:
输入密钥K,输出子密钥K1与K2。过程如下:
①用密钥K对全零输入块加密得到L
②K1通过如下方式导出:
如果L的最高有效位为0,则K1:=L左移1位;否则,K1等于const_Rb与左移1位的L异或。
③K2通过如下方式导出:
如果K1的最高有效位为0,则K2:=K1左移1位;否则,K2等于const_Rb与左移1位的K1异或。

AES-CMAC算法实现与测试

根据参考文献[1]javafr_RFC_4493给出的说明及测试实例,编写AES_CMAC类。在main()函数中加入取消注释宏定义AES_CMAC_TEST,进行AES-CMAC测试。
待测试消息是:
M= 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10

密钥是:
K= 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c

注意,测试中我们不是把M作为输入,而是截取其一部分作为输入消息,以测试AES_CMAC类处理不同长度的消息的正确性。测试结果如图:
这里写图片描述

对照参考文献[1]:
这里写图片描述
结果符合预期。
aes_cmac.h:

/************************************************************************/
/*AES_CMAC:     Use AES-128
/*Author:       chenweiliang
/*Version:      1.0
/*Reference:    javafr_RFC_4493
/************************************************************************/
#pragma once
#include "aes.h"

class AES_CMAC
{
public:
    //key:  128-bit key
    AES_CMAC(const byte key[]);

    virtual ~AES_CMAC();
    static const byte const_Rb[16];
    static const byte const_Zero[16];

    //msg:  message to be authenticated
    //len:  length of the message in bytes
    //mac:  message authentication code
    void getCMAC(byte *msg, int len, byte *mac);

    //Copy K1 to key1
    void getK1(byte *key1);

    //copy K2 to key2
    void getK2(byte *key2);

protected:
private:
    AES mAes_128;
    //128-bit key
    byte mK[16];
    //128-bit first subkey
    byte mK1[16];
    //128-bit second subkey
    byte mK2[16];

    //Utils function
    void leftShiftOneBit(byte *input_128,byte *output_128);
    void xor128(const byte *a, const byte *b, byte *out);

    void generateSubKey();
    //lastb:    last block of the intput message
    //pad:      last block after padding
    //length:   length of last block before padding
    void padding(byte *lastb,byte *pad,int length);

};

aes_cmac.cpp:

/************************************************************************/
/*AES_CMAC:     Use AES-128
/*Author:       chenweiliang
/*Version:      1.0
/*Reference:    javafr_RFC_4493
/************************************************************************/
#include "aes_cmac.h"

const byte AES_CMAC::const_Rb[16] = {
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x87
};

const byte AES_CMAC::const_Zero[16] = {
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
};

void AES_CMAC::leftShiftOneBit(byte *input_128, byte *output_128)
{
    byte overflow = 0x00;
    for (int i = 15; i >= 0; i--){
        output_128[i] = input_128[i] << 1;
        output_128[i] |= overflow;
        overflow = (input_128[i] & 0x80) ? 0x01 : 0x00;
    }
    return;
}

void AES_CMAC::xor128(const byte *a, const byte *b, byte *out)
{
    for (int i = 0; i < 16; i++){
        out[i] = a[i] ^ b[i];
    }
}

AES_CMAC::AES_CMAC(const byte key[])
{
    for (int i = 0; i < 16; i++){
        mK[i] = key[i];
    }
    mAes_128 = AES(mK,KEYLENGTH::KEY_LENGTH_16BYTES);

    generateSubKey();
}

void AES_CMAC::generateSubKey()
{
    //for output of AES-128 applied to 0^128
    byte L[16];
    //0^128
    byte Z[16];

    byte temp[16];

    for (int i = 0; i < 16; i++){
        Z[i] = 0;
    }

    mAes_128.encrypt(Z,L);
    //If MSB(L)==0,then mK1=L<<1
    if ((L[0] & 0x80) == 0){
        leftShiftOneBit(L, mK1);
    }
    //mK1=(L<<1)xor(Rb)
    else{
        leftShiftOneBit(L, temp);
        xor128(temp, const_Rb, mK1);
    }

    if ((mK1[0] & 0x80) == 0){
        leftShiftOneBit(mK1, mK2);
    }
    else{
        leftShiftOneBit(mK1, temp);
        xor128(temp, const_Rb, mK2);
    }
    return;
}

void AES_CMAC::padding(byte *lastb, byte *pad, int length)
{
    for (int i = 0; i < 16; i++){
        if (i < length){
            pad[i] = lastb[i];
        }
        else if (i == length){
            pad[i] = 0x80;
        }
        else{
            pad[i] = 0x00;
        }
    }
}

void AES_CMAC::getCMAC(byte *msg, int len, byte *mac)
{
    byte X[16];
    byte Y[16];
    byte msgLast[16];

    byte padded[16];

    //the number of blocks to be processed
    int n;
    //the number of bytes of the last block
    int r;
    //denoting last block is complete or not
    bool isComplete;

    n = (len + 15) / 16;

    if (n == 0){
        n = 1;
        isComplete = false;
    }
    else{
        if ((len % 16) == 0){
            isComplete = true;
        }
        else{
            isComplete = false;
        }
    }

    if (isComplete){
        xor128(&msg[16*(n-1)], mK1, msgLast);
    }
    else{
        padding(&msg[16*(n-1)], padded, len%16);
        xor128(padded, mK2, msgLast);
    }

    for (int i = 0; i < 16; i++){
        X[i] = 0;
    }
    for (int i = 0; i < n - 1; i++){
        //Y=(X)xor(meg_i)
        xor128(X, &msg[16 * i], Y);
        //X=AES(key,Y)
        mAes_128.encrypt(Y, X);
    }

    xor128(X, msgLast, Y);
    mAes_128.encrypt(Y, X);

    for (int i = 0; i < 16; i++){
        mac[i] = X[i];
    }

}

void AES_CMAC::getK1(byte *key1)
{
    for (int i = 0; i < 16; i++){
        key1[i] = mK1[i];
    }
}

void AES_CMAC::getK2(byte *key2)
{
    for (int i = 0; i < 16; i++){
        key2[i] = mK2[i];
    }
}

AES_CMAC::~AES_CMAC()
{

}

main函数:

/************************************************************************/
/*Main:         Test AES,CMAC
/*Author:       chenweiliang
/*Version:      1.0
/*Note:         To test AES-128,please comment the macro definition
/*              "#define AES_CMAC_TEST" and uncomment it for CMAC testing
/************************************************************************/

#include "aes.h"
#include "aes_cmac.h"
#ifndef AES_CMAC_TEST
#define AES_CMAC_TEST
#endif // !AES_CMAC_TEST


#include <string>
void print128(unsigned char state[16]){
    for (int i = 0; i < 16; i++){
        printf("%02hhx", state[i]);
        if (i % 8 == 7){
            printf(" ");
        }
    }
    printf("\n");
}

void print512(unsigned char data[64]){
    for (int i = 0; i < 64; i++){
        printf("%02hhx", data[i]);
        if (i % 8 == 7){
            printf(" ");
        }
        if (i % 32 == 31){
            printf("\n");
        }
    }
    printf("\n");
}

void printn(unsigned char data[], int n)
{
    for (int i = 0; i < n; i++){
        printf("%02hhx", data[i]);
        if (i % 8 == 7){
            printf(" ");
        }
        if (i % 16 ==15&&i!=n-1){
            printf("\n               ");
        }
        else if (i == n - 1){
            printf("\n");
        }
    }
}

using namespace std;
int main()
{

#ifdef AES_CMAC_TEST

    unsigned char K1[16], K2[16], mac[16], TT[12];
    unsigned char M[64] = {
        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
        0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
        0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
        0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
        0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
        0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
        0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
        0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
    };
    unsigned char key[16] = {
        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
        0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
    };

    AES_CMAC aes_cmac = AES_CMAC(key);

    printf("AES-CMAC Test\n");
    printf("-------------------------------------------------------------------\n");
    printf("SubKeyGeneration\n");
    printf("K              ");
    print128(key);
    printf("K1             ");
    aes_cmac.getK1(K1);
    print128(K1);
    printf("K2             ");
    aes_cmac.getK2(K2);
    print128(K2);
    printf("-------------------------------------------------------------------\n");
    printf("\n-------------------------------------------------------------------\n");
    aes_cmac.getCMAC(M, 0, mac);
    printf("Example 1:     len=0\n");
    printf("M              <Empty String>\n");
    printf("AES-CMAC:      ");
    print128(mac);
    printf("-------------------------------------------------------------------\n");

    aes_cmac.getCMAC(M, 16, mac);
    printf("Example 2:     len=16\n");
    printf("M              ");
    printn(M, 16);
    printf("AES-CMAC:      ");
    print128(mac);
    printf("-------------------------------------------------------------------\n");
    aes_cmac.getCMAC(M, 40, mac);
    printf("Example 3:     len=40\n");
    printf("M              ");
    printn(M, 40);
    printf("AES-CMAC:      ");
    print128(mac);
    printf("-------------------------------------------------------------------\n");
    aes_cmac.getCMAC(M, 64, mac);
    printf("Example 4:     len=64\n");
    printf("M              ");
    printn(M, 64);
    printf("AES-CMAC:      ");
    print128(mac);
    printf("-------------------------------------------------------------------\n");
#else
    unsigned char input[16] =
    {
    0x01, 0x23, 0x45, 0x67,
    0x89, 0xab, 0xcd, 0xef,
    0xfe, 0xdc, 0xba, 0x98,
    0x76, 0x54, 0x32, 0x10
    };
    unsigned char key[] =
    {
    0x0f, 0x15, 0x71, 0xc9,
    0x47, 0xd9, 0xe8, 0x59,
    0x0c, 0xb7, 0xad, 0xd6,
    0xaf, 0x7f, 0x67, 0x98
    };
    printf("AES Test\n");
    AES aes = AES(key, KEYLENGTH::KEY_LENGTH_16BYTES);
    printf("Key length:          128\n");
    printf("key:                 ");
    print128(key);
    printf("Message:             ");
    print128(input);
    byte out[16];
    aes.encrypt(input, out);
    printf("ciphertext:          ");
    print128(out);
    aes.decrypt(out,out);
    printf("plaintext:           ");
    print128(out);


#endif // AES_CMAC_TEST

    return 0;
}

参考文献

[1] javafr_RFC_4493
[2]AES加密学习笔记http://blog.csdn.net/whycadi/article/details/6917394
[3]AES加密算法http://www.cnblogs.com/mingcn/archive/2010/10/31/aes_c.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u014230646/article/details/79552843

智能推荐

Python 安全编程学习总结_python you must specify a valid interface name.-程序员宅基地

文章浏览阅读6.3k次,点赞2次,收藏15次。Python黑帽编程-ARP之一http://blog.csdn.net/supercooly/article/details/53956494参考文章:https://zhuanlan.zhihu.com/p/24645819参考文章:http://bbs.ichunqiu.com/thread-13429-1-1.html一、ARP协议中文名称:地址解析协议_python you must specify a valid interface name.

【solo】环境配置_深度学习solo算法必须要安装mmcv吗-程序员宅基地

文章浏览阅读329次。此帖记录一下SOLO环境配置的过程以及一些问题和解决办法。_深度学习solo算法必须要安装mmcv吗

模板与泛型编程_模版是复用代码的一中机制,利用模版可以进行与类型无关的程序设计-程序员宅基地

文章浏览阅读239次。泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。模板的格式template<typename T1, typename T2,…,typename Tn>返回值类型 函数名(参数列表){}模板分为类模板和函数模板模板它本身并不是函数,是编译..._模版是复用代码的一中机制,利用模版可以进行与类型无关的程序设计

如何将访问的接口去掉token验证_不通微服务开发阶段怎样取消sa_token验证-程序员宅基地

文章浏览阅读9.9k次。如何将访问的接口去掉token验证项目应用:springboot oath2完成此操作需要修改两个模块的application.yml文件修改,一个是getway网关的yml文件,另一个则是接口所在模块的yml文件_不通微服务开发阶段怎样取消sa_token验证

解决SVN Can’t open file ‘/XXX/xxx/db/txn-current-lock’错误_svn cant open file-程序员宅基地

文章浏览阅读1.8w次。今天用非root账号,用sudo 命令 添加了两个账号,然后重启服务···可以正常取出东西,但是不能提交···报的错就是 : 不能打开文件“/var/svn/svnrepos/db/txn-current-lock”: 权限不够查了半天 找到了解决办法,原文链接:http://blog.sina.com.cn/s/blog_7139569d0100woar.htm_svn cant open file

Microsoft Visual Studio 2019正式版离线安装包下载_microsoft vcredist 2019-程序员宅基地

文章浏览阅读2.4w次,点赞11次,收藏44次。原文地址:https://www.bitecho.net/microsoft-visual-studio-2019.html#respondVisual Studio(简称VS)是微软公司的开发工具包系列产品,包括了整个软件生命周期中所需要的大部分工具,如UML工具、代码管控工具、集成开发环境(IDE)等等,并且几乎适用于所有开发平台。微软于4月3日正式发布了Visual Studio 2019正式版,在IDE、性能、常规调试、源控制和Team管理器、编程语言、Web技术、Xamarin移动..._microsoft vcredist 2019

随便推点

深度学习目标检测系列之YOLO9000_yolov9-程序员宅基地

文章浏览阅读2.7k次,点赞6次,收藏6次。1.闲言 在正式的学习之前,我喜欢先放飞一下自我。我觉得技术就是用来聊的,找个酒馆,找些大神,咱们听着音乐一起聊起来。所以我特别希望能把自己的微博写的口语化,就像玩一样。就像古代那些说书人一样,萧远山和慕容博相视一笑,王图霸业,血海深仇,尽归尘土。这是我向往的一种表达方式,但是我现在还达不到那个境界,只能尽力而为吧。2.YOLOV2 1.十个改造点 yolov1提升了目标检测的速度,但是在MAP方面却掉了上去。所以说铁打的大神,流水的模型..._yolov9

自学微信二次开发(1)_微信 电脑端 二次开发-程序员宅基地

文章浏览阅读1.1w次,点赞8次,收藏5次。在二次开发微信,首先要成为微信开发者,在微信开发者平台注册,还要注册一个微信公众号,注册过程很简单,唯一的要求就是要绑定银行卡的微信账号扫一下二维码.然后就能注册成功微信公众号了. 在注册微信公众号之后有两种模式,一个是编辑模式,还有一个就是开发者模式.编辑模式其实也是一个后台操作的过程,在编辑模式下基本上一些基本的功能都能实现,像关键字回复,被关注的时候回复,还有订阅号每天能够推送一条信息给关注_微信 电脑端 二次开发

测试用例集构建_第2部分。为生产前测试构建测试用例-程序员宅基地

文章浏览阅读456次。管理生产的操作要求 内容系列: 此内容是#在系列的一部分#: 管理生产操作要求 https://www.ibm.com/developerworks/library/?series_title_by=manage+operational+requirements+for+production 请继续关注本系列中的其他内容。 该内容是该系列的一部分: 管理生产的操作需..._构建测评用例

Android 获取当前日期 时间很方便 直接调用Calendar类即可-程序员宅基地

文章浏览阅读282次。Android 获取当前日期 时间很方便 直接调用Calendar类即可定义变量private int mHour; private int mMinute; private int mYear; private int mMonth; private int mDay;调用如下 final Calendar c..._直接调用calendar类年份

leetcode 16 最接近的三数之和_最接近的三数之和 时间复杂度-程序员宅基地

文章浏览阅读145次。据说这个题很容易考,嘻嘻嘻,我自己做做。。。每做一个题 它还有变形,我又要开始思考。。。。。所有算法题里面我最讨厌 矩阵 矩阵,数学都没有搞明白矩阵,你还要我写代码。。。说明我今年变聪明了。。。。我做题都是非常简单思路,大道至简。就是如果这三个数之和刚好与target相等就直接输出,如果不是 就看范围。找到距离这个target最近的两个数。min,max最后用绝对值比较一下就可以了..._最接近的三数之和 时间复杂度

理解傅里叶变换算法(一)_求序列的5点dft-程序员宅基地

文章浏览阅读710次。经典算法研究系列:十、从头到尾彻底理解傅里叶变换算法、上作者:July、dznlong 二零一一年二月二十日推荐阅读:The Scientist and Engineer's Guide to Digital Signal Processing,By Steven W. Smith, Ph.D。此书地址:http://www.dspguide.com/pdfbook.htm_求序列的5点dft

推荐文章

热门文章

相关标签