Khan's Notebook GCC/GNU/Linux Delphi/Window Java/Anywhere

路漫漫,长修远,我们不能没有钱
随笔 - 172, 文章 - 0, 评论 - 257, 引用 - 0
数据加载中……

ChaCha20 加密 算法 c++11实现


ChaCha20 加密/解密算法, 支持单轮多轮加密
  1 #ifndef CHACHA20_H
  2 #define CHACHA20_H
  3 
  4 #include <array>
  5 #include <vector>
  6 #include <algorithm>
  7 #include <sstream>
  8 #include <iostream>
  9 #include <iomanip>
 10 #include <string>
 11 
 12 //#include<QDebug>
 13 
 14 
 15 /**
 16  * @brief ChaCha20 加密/解密算法, 支持单轮多轮加密
 17  */
 18 class ChaCha20
 19 {
 20 public:
 21     /**
 22      * @brief ChaCha20 chacha20加密算法构造器 单轮
 23      * @param key 32字节的key
 24      * @param nonce 8字节随机数
 25      */
 26     ChaCha20(std::array<unsigned char32> key, std::array<unsigned char8> nonce) :
 27         matrix{}
 28     {
 29         // 16byte常量字符串"apxe3 dnyb-2k et"
 30         this->matrix[ 0= 0x61707865;
 31         this->matrix[ 1= 0x3320646e;
 32         this->matrix[ 2= 0x79622d32;
 33         this->matrix[ 3= 0x6b206574;
 34 
 35         this->matrix[ 4= littleEndianToInt(key, 0);
 36         this->matrix[ 5= littleEndianToInt(key, 4);
 37         this->matrix[ 6= littleEndianToInt(key, 8);
 38         this->matrix[ 7= littleEndianToInt(key, 12);
 39 
 40         this->matrix[ 8= littleEndianToInt(key, 16);
 41         this->matrix[ 9= littleEndianToInt(key, 20);
 42         this->matrix[10= littleEndianToInt(key, 24);
 43         this->matrix[11= littleEndianToInt(key, 28);
 44 
 45         this->matrix[12= 0;
 46         this->matrix[13= 0;
 47         this->matrix[14= littleEndianToInt(nonce, 0);
 48         this->matrix[15= littleEndianToInt(nonce, 4);
 49     }
 50 
 51     /**
 52      * @brief ChaCha20 ChaCha20 chacha20加密算法构造器, 多轮
 53      * @param key 32字节key
 54      * @param nonce 12字节随机数
 55      * @param counter 轮转次数
 56      */
 57     ChaCha20(std::array<unsigned char32> key, std::array<unsigned char12> nonce, const int counter) :
 58         matrix{}
 59     {
 60 
 61         // 16byte常量字符串"apxe3 dnyb-2k et"
 62         this->matrix[ 0= 0x61707865;
 63         this->matrix[ 1= 0x3320646e;
 64         this->matrix[ 2= 0x79622d32;
 65         this->matrix[ 3= 0x6b206574;
 66 
 67         this->matrix[ 4= littleEndianToInt(key, 0);
 68         this->matrix[ 5= littleEndianToInt(key, 4);
 69         this->matrix[ 6= littleEndianToInt(key, 8);
 70         this->matrix[ 7= littleEndianToInt(key, 12);
 71         this->matrix[ 8= littleEndianToInt(key, 16);
 72         this->matrix[ 9= littleEndianToInt(key, 20);
 73         this->matrix[10= littleEndianToInt(key, 24);
 74         this->matrix[11= littleEndianToInt(key, 28);
 75 
 76 
 77         this->matrix[12= counter;
 78         this->matrix[13= littleEndianToInt(nonce, 0);
 79         this->matrix[14= littleEndianToInt(nonce, 4);
 80         this->matrix[15= littleEndianToInt(nonce, 8);
 81     }
 82 
 83 
 84     /**
 85      * @brief encrypt 加密
 86      * @param plain 原文
 87      * @param dst 密文
 88      * @param len 原文长度
 89      */
 90     void encrypt( std::vector<unsigned char>& plain, std::vector<unsigned char>& ciphertext) {
 91         std::array<int16> x{};
 92         std::array<unsigned char64> output{};
 93         int i, dpos = 0, spos = 0, len = plain.size();
 94 //        qDebug() << "len:" << len;
 95         while (len > 0) {
 96 //            for (i = 16; i-- > 0; ) x[i] = this->matrix[i];
 97             std::copy_n(matrix.begin(), matrix.size(), x.begin());
 98             for (i = 20; i > 0; i -= 2) {
 99                 quarterRound(x, 04,  812);
100                 quarterRound(x, 15,  913);
101                 quarterRound(x, 261014);
102                 quarterRound(x, 371115);
103                 quarterRound(x, 051015);
104                 quarterRound(x, 161112);
105                 quarterRound(x, 27,  813);
106                 quarterRound(x, 34,  914);
107 
108 //                qDebug() << "[" << intToHex(x[0]).c_str() << ", " << intToHex(x[1]).c_str() << ", " << intToHex(x[2]).c_str() << ", " << intToHex(x[3]).c_str()
109 //                         << ", " << intToHex(x[4]).c_str() << ", " << intToHex(x[5]).c_str() << ", " << intToHex(x[6]).c_str() << ", " << intToHex(x[7]).c_str()
110 //                         << ", " << intToHex(x[8]).c_str() << ", " << intToHex(x[9]).c_str() << ", " << intToHex(x[10]).c_str() << ", " << intToHex(x[11]).c_str()
111 //                         << ", " << intToHex(x[12]).c_str() << ", " << intToHex(x[13]).c_str() << ", " << intToHex(x[14]).c_str() << ", " << intToHex(x[15]).c_str()  << "]";
112             }
113             for (i = 16; i-- > 0; ) x[i] += this->matrix[i];
114             for (i = 16; i-- > 0; ) intToLittleEndian(x[i], output, 4 * i);
115 
116             // TODO: (1) check block count 32-bit vs 64-bit; (2) java int is signed!
117             this->matrix[12+= 1;
118             if (this->matrix[12== 0) {
119                 this->matrix[13+= 1;
120             }
121             if (len <= 64) {
122                 for (i = len; i-- > 0; ) {
123 //                    qDebug() << "dist["  << (i + dpos) << "] = " << ((unsigned char) (plain[i + spos] ^ output[i]));
124                     ciphertext[i + dpos] = (unsigned char) (plain[i + spos] ^ output[i]);
125                 }
126                 return;
127             }
128             for (i = 64; i-- > 0; ) {
129 //                qDebug() << "dist[" << (i + dpos) << "] = " << ((unsigned char) (plain[i + spos] ^ output[i]));
130                 ciphertext[i + dpos] = (unsigned char) (plain[i + spos] ^ output[i]);
131             }
132             len -= 64;
133             spos += 64;
134             dpos += 64;
135         }
136     }
137 
138 
139 
140     /**
141      * @brief decrypt 解密
142      * @param src 原文
143      * @param plain 密文
144      * @param len
145      */
146     void decrypt(std::vector<unsigned char>& ciphertext, std::vector<unsigned char>& plain) {
147         encrypt(ciphertext, plain);
148     }
149 
150 
151 //    void test(){
152 //        int v = 0xaaaa, c=0x8888;
153 //        rotate(v, c);
154 //    }
155 
156 
157 protected:
158     template< typename T >
159     std::string intToHex( const T& i )
160     {
161       std::stringstream stream;
162       stream //<< "0x"
163              << std::setfill ('0'<< std::setw(sizeof(T)*2)
164              << std::hex << i;
165       return stream.str();
166     }
167 
168 private:
169     /**
170      * N支持8或12
171      */
172     template<size_t N >
173     /**
174      * @brief littleEndianToInt 小端内存块转整数
175      * @param bs  小端内存块
176      * @param offset 需要转换成int的位置offset
177      * @return 返回转换后的整数
178      */
179     inline int littleEndianToInt(std::array<unsigned char, N>& bs, const int& offset){
180         return (bs[offset]) | (bs[offset + 1<< 8| (bs[offset + 2<< 16| (bs[offset + 3<< 24);
181     }
182 
183 
184     /**
185      * @brief intToLittleEndian
186      * @param n 整数
187      * @param bs 小端内存块, 出参
188      * @param offset 存放在内存块中的偏移位
189      */
190     inline void intToLittleEndian(const int& n, std::array<unsigned char64>& bs, int offset){
191         bs[  offset] = (unsigned char)(n      );
192         bs[++offset] = (unsigned char)((unsigned)n >>  8);
193         bs[++offset] = (unsigned char)((unsigned)n >> 16);
194         bs[++offset] = (unsigned char)((unsigned)n >> 24);
195     }
196 
197 
198     int rotate(const int& v, const int& c){
199 //        qDebug() << intToHex((v << c) | (v >> (32 - c))).c_str();
200         return (v << c) | ((unsigned)v >> (32 - c));
201     }
202 
203     void quarterRound(std::array<int16>& x, const int& a, const int& b, const int& c, const int& d) {
204 //        qDebug() << "begin " << intToHex(x[a]).c_str() << ", "<< intToHex(x[b]).c_str() << ", "<< intToHex(x[c]).c_str() << ", "<< intToHex(x[d]).c_str();
205         x[a] += x[b];
206 //        qDebug() << "1 " << intToHex(x[a]).c_str() << ", "<< intToHex(x[b]).c_str() << ", "<< intToHex(x[c]).c_str() << ", "<< intToHex(x[d]).c_str();
207 
208 //        qDebug() << "1 " << intToHex(x[d]).c_str() << ",  " << intToHex(x[d] ^ x[a]).c_str();
209         x[d] = rotate(x[d] ^ x[a], 16);
210 //        qDebug() << "1 " << intToHex(x[d]).c_str() << ",  " << intToHex(x[d] ^ x[a]).c_str();
211         x[c] += x[d];
212 //        qDebug() << "2 " << intToHex(x[a]).c_str() << ", "<< intToHex(x[b]).c_str() << ", "<< intToHex(x[c]).c_str() << ", "<< intToHex(x[d]).c_str();
213 
214         x[b] = rotate(x[b] ^ x[c], 12);
215 //        qDebug() << "2 " << intToHex(x[b]).c_str() ;
216         x[a] += x[b];
217         x[d] = rotate(x[d] ^ x[a], 8);
218         x[c] += x[d];
219         x[b] = rotate(x[b] ^ x[c], 7);
220 //        qDebug() << "end " << intToHex(x[a]).c_str() << ", "<< intToHex(x[b]).c_str() << ", "<< intToHex(x[c]).c_str() << ", "<< intToHex(x[d]).c_str();
221     }
222 
223 
224 private:
225     std::array<int16> matrix;
226 
227 };
228 
229 
230 
231 
232 #endif // CHACHA20_H
233 






调用方式:

1  加密:

 1         volatile bool flag = false;
 2         //加密
 3         QByteArray qKeyBytes = qKey.toUtf8();
 4         std::array<unsigned char32> keyBytes;
 5         std::copy_n(qKeyBytes.begin(), keyBytes.size(), keyBytes.begin());
 6 
 7         QByteArray qSourceBytes = qSource.toUtf8();
 8         std::vector<unsigned char> sourceBytes(qSourceBytes.size());
 9         std::copy_n(qSourceBytes.begin(), qSourceBytes.size(), sourceBytes.begin());
10 
11         std::vector<unsigned char> destinaBytes(sourceBytes.size());
12         if(qNonce.length() == 8){
13             QByteArray qNonceBytes = qNonce.toUtf8();
14             std::array<unsigned char8> nonceBytes;
15             std::copy_n(qNonceBytes.begin(), qNonceBytes.size(), nonceBytes.begin());
16             ChaCha20 chacha20(keyBytes, nonceBytes);
17             //            chacha20.test();
18             chacha20.encrypt(sourceBytes, destinaBytes);
19             flag = true;
20         } else if(qNonce.length() == 12){
21             QByteArray qNonceBytes = qNonce.toUtf8();
22             std::array<unsigned char12> nonceBytes;
23             std::copy_n(qNonceBytes.begin(), qNonceBytes.size(), nonceBytes.begin());
24             ChaCha20 chacha20(keyBytes, nonceBytes, qCounter.toUInt());
25             //            chacha20.test();
26             chacha20.encrypt(sourceBytes, destinaBytes);
27             flag = true;
28         } else {
29             qDebug() << TIMESTAMP << "encrypt nonce长度不合法";
30         }
31 
32         if(flag){
33             QByteArray qDestBytes(destinaBytes.size(), 0);
34             std::copy_n(destinaBytes.begin(), destinaBytes.size(), qDestBytes.begin());
35 
36             qDebug() << TIMESTAMP << "encrypt data:" << qDestBytes.toHex();
37 
38             QString qDestStr = qDestBytes.toBase64(QByteArray::Base64UrlEncoding);
39             qDebug() << TIMESTAMP << "encrypt base64 data:" << qDestStr;
40 
41             ui->leDestina->setText(qDestStr);
42         }



2: 解密
            volatile bool flag = false;
            //解密
            QByteArray qKeyBytes = qKey.toUtf8();
            std::array<unsigned char, 32> keyBytes;
            std::copy_n(qKeyBytes.begin(), keyBytes.size(), keyBytes.begin());

            QByteArray qDestinaBytes;
            qDestinaBytes = qDestinaBytes.fromBase64(qDestina.toUtf8());
    //        qDebug() << TIMESTAMP << "ciphertext data:" << qDestinaBytes.toHex();

            std::vector<unsigned char> destinaBytes(qDestinaBytes.size());
            std::copy_n(qDestinaBytes.begin(), qDestinaBytes.size(), destinaBytes.begin());

            std::vector<unsigned char> sourceBytes(destinaBytes.size());
            if(qNonce.length() == 8){
                QByteArray qNonceBytes = qNonce.toUtf8();
                std::array<unsigned char, 8> nonceBytes;
                std::copy_n(qNonceBytes.begin(), qNonceBytes.size(), nonceBytes.begin());
                ChaCha20 chacha20(keyBytes, nonceBytes);
                chacha20.decrypt(destinaBytes, sourceBytes);
                flag = true;
            } else if(qNonce.length() == 12){
                QByteArray qNonceBytes = qNonce.toUtf8();
                std::array<unsigned char, 12> nonceBytes;
                std::copy_n(qNonceBytes.begin(), qNonceBytes.size(), nonceBytes.begin());
                ChaCha20 chacha20(keyBytes, nonceBytes, qCounter.toUInt());
                chacha20.decrypt(destinaBytes, sourceBytes);
                flag = true;
            } else {
                qDebug() << TIMESTAMP << "decrypt nonce长度不合法";
            }

            if(flag){
                QByteArray qSourceBytes(sourceBytes.size(), 0);
                std::copy_n(sourceBytes.begin(), sourceBytes.size(), qSourceBytes.begin());

                qDebug() << TIMESTAMP << "decrypt ascii data:" << qSourceBytes.toHex();

                QString qSourceStr = qSourceBytes;
                qDebug() << TIMESTAMP << "decrypt data:" << qSourceStr;

                ui->leSource->setText(qSourceStr);
            }


            //解密
            qDebug() << TIMESTAMP << "key:" << qKey << ", nonce:" << qNonce << ", counter:" << qCounter
                     << ", destina:" << qDestina;



后记 :
1. nonce长度只允许8字节或12字节, 如果nonce长度为8字节, 则counter被无视, 如果为12字节, counter才有效
2. key长度恒定为32字节, 如果不足, 自己补齐32字节
3. 解密就是将加密的结果在xor一次





















posted on 2021-06-03 17:38 Khan 阅读(2617) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理