1 /*
2 * This file is part of the openHiTLS project.
3 *
4 * openHiTLS is licensed under the Mulan PSL v2.
5 * You can use this software according to the terms and conditions of the Mulan PSL v2.
6 * You may obtain a copy of Mulan PSL v2 at:
7 *
8 * http://license.coscl.org.cn/MulanPSL2
9 *
10 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13 * See the Mulan PSL v2 for more details.
14 */
15
16 #include "hitls_build.h"
17 #ifdef HITLS_CRYPTO_CHACHA20
18
19 #include "securec.h"
20 #include "bsl_err_internal.h"
21 #include "crypt_utils.h"
22 #include "crypt_errno.h"
23 #include "crypt_chacha20.h"
24 #include "chacha20_local.h"
25
26 #define KEYSET 0x01
27 #define NONCESET 0x02
28
29 // RFC7539-2.1
30 #define QUARTER(a, b, c, d) \
31 do { \
32 (a) += (b); (d) ^= (a); (d) = ROTL32((d), 16); \
33 (c) += (d); (b) ^= (c); (b) = ROTL32((b), 12); \
34 (a) += (b); (d) ^= (a); (d) = ROTL32((d), 8); \
35 (c) += (d); (b) ^= (c); (b) = ROTL32((b), 7); \
36 } while (0)
37
38 #define QUARTERROUND(state, a, b, c, d) QUARTER((state)[(a)], (state)[(b)], (state)[(c)], (state)[(d)])
39
CRYPT_CHACHA20_SetKey(CRYPT_CHACHA20_Ctx * ctx,const uint8_t * key,uint32_t keyLen)40 int32_t CRYPT_CHACHA20_SetKey(CRYPT_CHACHA20_Ctx *ctx, const uint8_t *key, uint32_t keyLen)
41 {
42 if (ctx == NULL || key == NULL || keyLen == 0) {
43 BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
44 return CRYPT_NULL_INPUT;
45 }
46 if (keyLen != CHACHA20_KEYLEN) {
47 BSL_ERR_PUSH_ERROR(CRYPT_CHACHA20_KEYLEN_ERROR);
48 return CRYPT_CHACHA20_KEYLEN_ERROR;
49 }
50 /**
51 * RFC7539-2.3
52 * cccccccc cccccccc cccccccc cccccccc
53 * kkkkkkkk kkkkkkkk kkkkkkkk kkkkkkkk
54 * kkkkkkkk kkkkkkkk kkkkkkkk kkkkkkkk
55 * bbbbbbbb nnnnnnnn nnnnnnnn nnnnnnnn
56 */
57 // The first four words (0-3) are constants: 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574
58 ctx->state[0] = 0x61707865;
59 ctx->state[1] = 0x3320646e;
60 ctx->state[2] = 0x79622d32;
61 ctx->state[3] = 0x6b206574;
62 /**
63 * The next eight words (4-11) are taken from the 256-bit key by
64 * reading the bytes in little-endian order, in 4-byte chunks.
65 */
66 ctx->state[4] = GET_UINT32_LE(key, 0);
67 ctx->state[5] = GET_UINT32_LE(key, 4);
68 ctx->state[6] = GET_UINT32_LE(key, 8);
69 ctx->state[7] = GET_UINT32_LE(key, 12);
70 ctx->state[8] = GET_UINT32_LE(key, 16);
71 ctx->state[9] = GET_UINT32_LE(key, 20);
72 ctx->state[10] = GET_UINT32_LE(key, 24);
73 ctx->state[11] = GET_UINT32_LE(key, 28);
74 // Word 12 is a block counter
75 // RFC7539-2.4: It makes sense to use one if we use the zero block
76 ctx->state[12] = 1;
77 ctx->set |= KEYSET;
78 ctx->lastLen = 0;
79 return CRYPT_SUCCESS;
80 }
81
CRYPT_CHACHA20_SetNonce(CRYPT_CHACHA20_Ctx * ctx,const uint8_t * nonce,uint32_t nonceLen)82 static int32_t CRYPT_CHACHA20_SetNonce(CRYPT_CHACHA20_Ctx *ctx, const uint8_t *nonce, uint32_t nonceLen)
83 {
84 // RFC7539-2.3
85 if (ctx == NULL || nonce == NULL) {
86 BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
87 return CRYPT_NULL_INPUT;
88 }
89 if (nonceLen != CHACHA20_NONCELEN) {
90 BSL_ERR_PUSH_ERROR(CRYPT_CHACHA20_NONCELEN_ERROR);
91 return CRYPT_CHACHA20_NONCELEN_ERROR;
92 }
93 /**
94 * Words 13-15 are a nonce, which should not be repeated for the same
95 * key. The 13th word is the first 32 bits of the input nonce taken
96 * as a little-endian integer, while the 15th word is the last 32
97 * bits.
98 */
99 ctx->state[13] = GET_UINT32_LE(nonce, 0);
100 ctx->state[14] = GET_UINT32_LE(nonce, 4);
101 ctx->state[15] = GET_UINT32_LE(nonce, 8);
102 ctx->set |= NONCESET;
103 ctx->lastLen = 0;
104 return CRYPT_SUCCESS;
105 }
106
107 // Little-endian data input
CRYPT_CHACHA20_SetCount(CRYPT_CHACHA20_Ctx * ctx,const uint8_t * cnt,uint32_t cntLen)108 static int32_t CRYPT_CHACHA20_SetCount(CRYPT_CHACHA20_Ctx *ctx, const uint8_t *cnt, uint32_t cntLen)
109 {
110 if (ctx == NULL || cnt == NULL) {
111 BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
112 return CRYPT_NULL_INPUT;
113 }
114 if (cntLen != sizeof(uint32_t)) {
115 BSL_ERR_PUSH_ERROR(CRYPT_CHACHA20_COUNTLEN_ERROR);
116 return CRYPT_CHACHA20_COUNTLEN_ERROR;
117 }
118 /**
119 * RFC7539-2.4
120 * This can be set to any number, but will
121 * usually be zero or one. It makes sense to use one if we use the
122 * zero block for something else, such as generating a one-time
123 * authenticator key as part of an AEAD algorithm
124 */
125 ctx->state[12] = GET_UINT32_LE((uintptr_t)cnt, 0);
126 ctx->lastLen = 0;
127 return CRYPT_SUCCESS;
128 }
129
CHACHA20_Block(CRYPT_CHACHA20_Ctx * ctx)130 void CHACHA20_Block(CRYPT_CHACHA20_Ctx *ctx)
131 {
132 uint32_t i;
133 // The length defined by ctx->last.c is the same as that defined by ctx->state.
134 // Therefore, the returned value is not out of range.
135 (void)memcpy_s(ctx->last.c, CHACHA20_STATEBYTES, ctx->state, sizeof(ctx->state));
136 /* RFC7539-2.3 These are 20 round in this function */
137 for (i = 0; i < 10; i++) {
138 /* column round */
139 QUARTERROUND(ctx->last.c, 0, 4, 8, 12);
140 QUARTERROUND(ctx->last.c, 1, 5, 9, 13);
141 QUARTERROUND(ctx->last.c, 2, 6, 10, 14);
142 QUARTERROUND(ctx->last.c, 3, 7, 11, 15);
143 /* diagonal round */
144 QUARTERROUND(ctx->last.c, 0, 5, 10, 15);
145 QUARTERROUND(ctx->last.c, 1, 6, 11, 12);
146 QUARTERROUND(ctx->last.c, 2, 7, 8, 13);
147 QUARTERROUND(ctx->last.c, 3, 4, 9, 14);
148 }
149 /* Reference from rfc 7539, At the end of 20 rounds (or 10 iterations of the above list),
150 * we add the original input words to the output words
151 */
152 for (i = 0; i < CHACHA20_STATESIZE; i++) {
153 ctx->last.c[i] += ctx->state[i];
154 ctx->last.c[i] = CRYPT_HTOLE32(ctx->last.c[i]);
155 }
156 ctx->state[12]++;
157 }
158
CRYPT_CHACHA20_Update(CRYPT_CHACHA20_Ctx * ctx,const uint8_t * in,uint8_t * out,uint32_t len)159 int32_t CRYPT_CHACHA20_Update(CRYPT_CHACHA20_Ctx *ctx, const uint8_t *in, uint8_t *out, uint32_t len)
160 {
161 if (ctx == NULL || out == NULL || in == NULL || len == 0) {
162 BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
163 return CRYPT_NULL_INPUT;
164 }
165 if ((ctx->set & KEYSET) == 0) {
166 BSL_ERR_PUSH_ERROR(CRYPT_CHACHA20_NO_KEYINFO);
167 return CRYPT_CHACHA20_NO_KEYINFO;
168 }
169 if ((ctx->set & NONCESET) == 0) {
170 BSL_ERR_PUSH_ERROR(CRYPT_CHACHA20_NO_NONCEINFO);
171 return CRYPT_CHACHA20_NO_NONCEINFO;
172 }
173 uint32_t i;
174 const uint8_t *offIn = in;
175 uint8_t *offOut = out;
176 uint32_t tLen = len;
177 if (ctx->lastLen != 0) { // has remaining data during the last processing
178 uint32_t num = (tLen < ctx->lastLen) ? tLen : ctx->lastLen;
179 uint8_t *tLast = ctx->last.u + CHACHA20_STATEBYTES - ctx->lastLen; // offset
180 for (i = 0; i < num; i++) {
181 offOut[i] = tLast[i] ^ offIn[i];
182 }
183 offIn += num;
184 offOut += num;
185 tLen -= num;
186 ctx->lastLen -= num;
187 }
188 if (tLen >= CHACHA20_STATEBYTES) { // which is greater than or equal to an integer multiple of 64 bytes
189 CHACHA20_Update(ctx, offIn, offOut, tLen); // processes data that is an integer multiple of 64 bytes
190 uint32_t vLen = tLen - (tLen & 0x3f); // 0x3f = %CHACHA20_STATEBYTES
191 offIn += vLen;
192 offOut += vLen;
193 tLen -= vLen;
194 }
195 // Process the remaining data
196 if (tLen > 0) {
197 CHACHA20_Block(ctx);
198 uint32_t t = tLen & 0xf8; // processing length is a multiple of 8
199 if (t != 0) {
200 DATA64_XOR(ctx->last.u, offIn, offOut, t);
201 }
202 for (i = t; i < tLen; i++) {
203 offOut[i] = ctx->last.u[i] ^ offIn[i];
204 }
205 ctx->lastLen = CHACHA20_STATEBYTES - tLen;
206 }
207 return CRYPT_SUCCESS;
208 }
209
CRYPT_CHACHA20_Ctrl(CRYPT_CHACHA20_Ctx * ctx,int32_t opt,void * val,uint32_t len)210 int32_t CRYPT_CHACHA20_Ctrl(CRYPT_CHACHA20_Ctx *ctx, int32_t opt, void *val, uint32_t len)
211 {
212 switch (opt) {
213 case CRYPT_CTRL_SET_IV: // in chacha20_poly1305 mode, the configured IV is the nonce of chacha20.
214 /**
215 * RFC_7539-2.8.1
216 * chacha20_aead_encrypt(aad, key, iv, constant, plaintext):
217 * nonce = constant | iv
218 */
219 return CRYPT_CHACHA20_SetNonce(ctx, val, len);
220 case CRYPT_CTRL_SET_COUNT:
221 return CRYPT_CHACHA20_SetCount(ctx, val, len);
222 default:
223 BSL_ERR_PUSH_ERROR(CRYPT_CHACHA20_CTRLTYPE_ERROR);
224 return CRYPT_CHACHA20_CTRLTYPE_ERROR;
225 }
226 }
227
CRYPT_CHACHA20_Clean(CRYPT_CHACHA20_Ctx * ctx)228 void CRYPT_CHACHA20_Clean(CRYPT_CHACHA20_Ctx *ctx)
229 {
230 if (ctx == NULL) {
231 return;
232 }
233
234 (void)memset_s(ctx, sizeof(CRYPT_CHACHA20_Ctx), 0, sizeof(CRYPT_CHACHA20_Ctx));
235 }
236 #endif // HITLS_CRYPTO_CHACHA20
237