forked from rweather/arduinolibs
-
Notifications
You must be signed in to change notification settings - Fork 4
/
ChaCha.cpp
281 lines (258 loc) · 8.71 KB
/
ChaCha.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/*
* Copyright (C) 2015 Southern Storm Software, Pty Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "ChaCha.h"
#include "Crypto.h"
#include "utility/RotateUtil.h"
#include "utility/EndianUtil.h"
#include "utility/ProgMemUtil.h"
#include <string.h>
/**
* \class ChaCha ChaCha.h <ChaCha.h>
* \brief ChaCha stream cipher.
*
* ChaCha is a stream cipher that takes a key, an 8-byte nonce/IV, and a
* counter and hashes them to generate a keystream to XOR with the plaintext.
* Variations on the ChaCha cipher use 8, 12, or 20 rounds of hashing
* operations with either 128-bit or 256-bit keys.
*
* Reference: http://cr.yp.to/chacha.html
*/
/**
* \brief Constructs a new ChaCha stream cipher.
*
* \param numRounds Number of encryption rounds to use; usually 8, 12, or 20.
*/
ChaCha::ChaCha(uint8_t numRounds)
: rounds(numRounds)
, posn(64)
{
}
ChaCha::~ChaCha()
{
clean(block);
clean(stream);
}
size_t ChaCha::keySize() const
{
// Default key size is 256-bit, but any key size is allowed.
return 32;
}
size_t ChaCha::ivSize() const
{
// We return 8 but we also support 12-byte nonces in setIV().
return 8;
}
/**
* \fn uint8_t ChaCha::numRounds() const
* \brief Returns the number of encryption rounds; usually 8, 12, or 20.
*
* \sa setNumRounds()
*/
/**
* \fn void ChaCha::setNumRounds(uint8_t numRounds)
* \brief Sets the number of encryption rounds.
*
* \param numRounds The number of encryption rounds; usually 8, 12, or 20.
*
* \sa numRounds()
*/
bool ChaCha::setKey(const uint8_t *key, size_t len)
{
static const char tag128[] PROGMEM = "expand 16-byte k";
static const char tag256[] PROGMEM = "expand 32-byte k";
if (len <= 16) {
memcpy_P(block, tag128, 16);
memcpy(block + 16, key, len);
memcpy(block + 32, key, len);
if (len < 16) {
memset(block + 16 + len, 0, 16 - len);
memset(block + 32 + len, 0, 16 - len);
}
} else {
if (len > 32)
len = 32;
memcpy_P(block, tag256, 16);
memcpy(block + 16, key, len);
if (len < 32)
memset(block + 16 + len, 0, 32 - len);
}
posn = 64;
return true;
}
bool ChaCha::setIV(const uint8_t *iv, size_t len)
{
// From draft-nir-cfrg-chacha20-poly1305-10.txt, we can use either
// 64-bit or 96-bit nonces. The 96-bit nonce consists of the high
// word of the counter prepended to a regular 64-bit nonce for ChaCha.
if (len == 8) {
memset(block + 48, 0, 8);
memcpy(block + 56, iv, len);
posn = 64;
return true;
} else if (len == 12) {
memset(block + 48, 0, 4);
memcpy(block + 52, iv, len);
posn = 64;
return true;
} else {
return false;
}
}
/**
* \brief Sets the starting counter for encryption.
*
* \param counter A 4-byte or 8-byte value to use for the starting counter
* instead of the default value of zero.
* \param len The length of the counter, which must be 4 or 8.
* \return Returns false if \a len is not 4 or 8.
*
* This function must be called after setIV() and before the first call
* to encrypt(). It is used to specify a different starting value than
* zero for the counter portion of the hash input.
*
* \sa setIV()
*/
bool ChaCha::setCounter(const uint8_t *counter, size_t len)
{
// Normally both the IV and the counter are 8 bytes in length.
// However, if the IV was 12 bytes, then a 4 byte counter can be used.
if (len == 4 || len == 8) {
memcpy(block + 48, counter, len);
posn = 64;
return true;
} else {
return false;
}
}
void ChaCha::encrypt(uint8_t *output, const uint8_t *input, size_t len)
{
while (len > 0) {
if (posn >= 64) {
// Generate a new encrypted counter block.
hashCore((uint32_t *)stream, (const uint32_t *)block, rounds);
posn = 0;
// Increment the counter, taking care not to reveal
// any timing information about the starting value.
// We iterate through the entire counter region even
// if we could stop earlier because a byte is non-zero.
uint16_t temp = 1;
uint8_t index = 48;
while (index < 56) {
temp += block[index];
block[index] = (uint8_t)temp;
temp >>= 8;
++index;
}
}
uint8_t templen = 64 - posn;
if (templen > len)
templen = len;
len -= templen;
while (templen > 0) {
*output++ = *input++ ^ stream[posn++];
--templen;
}
}
}
void ChaCha::decrypt(uint8_t *output, const uint8_t *input, size_t len)
{
encrypt(output, input, len);
}
/**
* \brief Generates a single block of output direct from the keystream.
*
* \param output The output buffer to fill with keystream bytes.
*
* Unlike encrypt(), this function does not XOR the keystream with
* plaintext data. Instead it generates the keystream directly into
* the caller-supplied buffer. This is useful if the caller knows
* that the plaintext is all-zeroes.
*
* \sa encrypt()
*/
void ChaCha::keystreamBlock(uint32_t *output)
{
// Generate the hash output directly into the caller-supplied buffer.
hashCore(output, (const uint32_t *)block, rounds);
posn = 64;
// Increment the lowest counter byte. We are assuming that the caller
// is ChaChaPoly::setKey() and that the previous counter value was zero.
block[48] = 1;
}
void ChaCha::clear()
{
clean(block);
clean(stream);
posn = 64;
}
// Perform a ChaCha quarter round operation.
#define quarterRound(a, b, c, d) \
do { \
uint32_t _b = (b); \
uint32_t _a = (a) + _b; \
uint32_t _d = leftRotate((d) ^ _a, 16); \
uint32_t _c = (c) + _d; \
_b = leftRotate12(_b ^ _c); \
_a += _b; \
(d) = _d = leftRotate(_d ^ _a, 8); \
_c += _d; \
(a) = _a; \
(b) = leftRotate7(_b ^ _c); \
(c) = _c; \
} while (0)
/**
* \brief Executes the ChaCha hash core on an input memory block.
*
* \param output Output memory block, must be at least 16 words in length
* and must not overlap with \a input.
* \param input Input memory block, must be at least 16 words in length.
* \param rounds Number of ChaCha rounds to perform; usually 8, 12, or 20.
*
* This function is provided for the convenience of applications that need
* access to the ChaCha hash core without the higher-level processing that
* turns the core into a stream cipher.
*/
void ChaCha::hashCore(uint32_t *output, const uint32_t *input, uint8_t rounds)
{
uint8_t posn;
// Copy the input buffer to the output prior to the first round
// and convert from little-endian to host byte order.
for (posn = 0; posn < 16; ++posn)
output[posn] = le32toh(input[posn]);
// Perform the ChaCha rounds in sets of two.
for (; rounds >= 2; rounds -= 2) {
// Column round.
quarterRound(output[0], output[4], output[8], output[12]);
quarterRound(output[1], output[5], output[9], output[13]);
quarterRound(output[2], output[6], output[10], output[14]);
quarterRound(output[3], output[7], output[11], output[15]);
// Diagonal round.
quarterRound(output[0], output[5], output[10], output[15]);
quarterRound(output[1], output[6], output[11], output[12]);
quarterRound(output[2], output[7], output[8], output[13]);
quarterRound(output[3], output[4], output[9], output[14]);
}
// Add the original input to the final output, convert back to
// little-endian, and return the result.
for (posn = 0; posn < 16; ++posn)
output[posn] = htole32(output[posn] + le32toh(input[posn]));
}