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 char, 32> key, std::array<unsigned char, 8> 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 char, 32> key, std::array<unsigned char, 12> 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<int, 16> x{};
92 std::array<unsigned char, 64> 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, 0, 4, 8, 12);
100 quarterRound(x, 1, 5, 9, 13);
101 quarterRound(x, 2, 6, 10, 14);
102 quarterRound(x, 3, 7, 11, 15);
103 quarterRound(x, 0, 5, 10, 15);
104 quarterRound(x, 1, 6, 11, 12);
105 quarterRound(x, 2, 7, 8, 13);
106 quarterRound(x, 3, 4, 9, 14);
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 char, 64>& 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<int, 16>& 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<int, 16> 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 char, 32> 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 char, 8> 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 char, 12> 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一次