• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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