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_SIPHASH
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include "securec.h"
22 #include "crypt_errno.h"
23 #include "crypt_utils.h"
24 #include "bsl_err_internal.h"
25 #include "bsl_sal.h"
26 #include "crypt_siphash.h"
27 #include "eal_mac_local.h"
28
29 #define SIPHASH_HALF_KEY_SIZE 8
30 #define BYTE_TO_BITS_RATIO 8
31 #define LROT_UINT64(num, bits) (uint64_t)(((num) << (bits)) | ((num) >> (64 - (bits))))
32 #define SIPHASH_SIX_OCTET_TO_BITS ((BYTE_TO_BITS_RATIO) * 6)
33 #define SIPHASH_FIVE_OCTET_TO_BITS ((BYTE_TO_BITS_RATIO) * 5)
34 #define SIPHASH_FOUR_OCTET_TO_BITS ((BYTE_TO_BITS_RATIO) * 4)
35 #define SIPHASH_THREE_OCTET_TO_BITS ((BYTE_TO_BITS_RATIO) * 3)
36 #define SIPHASH_TWO_OCTET_TO_BITS ((BYTE_TO_BITS_RATIO) * 2)
37 #define SIPHASH_ONE_OCTET_TO_BITS ((BYTE_TO_BITS_RATIO) * 1)
38
39 struct SIPHASH_Ctx {
40 uint64_t state0;
41 uint64_t state1;
42 uint64_t state2;
43 uint64_t state3;
44 uint16_t compressionRounds;
45 uint16_t finalizationRounds;
46 uint32_t hashSize;
47 uint32_t accInLen;
48 uint32_t offset;
49 uint8_t remainder[SIPHASH_WORD_SIZE];
50 };
51
BytesToUint64LittleEndian(const uint8_t key[SIPHASH_WORD_SIZE])52 static inline uint64_t BytesToUint64LittleEndian(const uint8_t key[SIPHASH_WORD_SIZE])
53 {
54 uint64_t ret = 0ULL;
55 for (uint32_t i = 0; i < SIPHASH_WORD_SIZE; i++) {
56 ret = ret | (((uint64_t)key[i]) << (i * BYTE_TO_BITS_RATIO));
57 }
58 return ret;
59 }
60
Uint64ToBytesLittleEndian(uint64_t src,uint8_t out[SIPHASH_WORD_SIZE])61 static void Uint64ToBytesLittleEndian(uint64_t src, uint8_t out[SIPHASH_WORD_SIZE])
62 {
63 for (uint32_t i = 0; i < SIPHASH_WORD_SIZE; i++) {
64 out[i] = (uint8_t)(src >> (i * BYTE_TO_BITS_RATIO));
65 }
66 }
67
DealLastWord(uint64_t lastWord,const uint8_t * bytes,size_t bytesLen)68 static uint64_t DealLastWord(uint64_t lastWord, const uint8_t *bytes, size_t bytesLen)
69 {
70 uint64_t tmpLastWord = lastWord;
71 switch (bytesLen) {
72 case 7:
73 // Do not need to run break from the case, fall through the switch-case.
74 // The remaining 7 bytes are to be processed and shift to left by 6 bytes.
75 tmpLastWord |= ((uint64_t)bytes[6]) << SIPHASH_SIX_OCTET_TO_BITS;
76 /* fall-through */
77 case 6:
78 tmpLastWord |= ((uint64_t)bytes[5]) << SIPHASH_FIVE_OCTET_TO_BITS;
79 /* fall-through */
80 case 5:
81 tmpLastWord |= ((uint64_t)bytes[4]) << SIPHASH_FOUR_OCTET_TO_BITS;
82 /* fall-through */
83 case 4:
84 tmpLastWord |= ((uint64_t)bytes[3]) << SIPHASH_THREE_OCTET_TO_BITS;
85 /* fall-through */
86 case 3:
87 tmpLastWord |= ((uint64_t)bytes[2]) << SIPHASH_TWO_OCTET_TO_BITS;
88 /* fall-through */
89 case 2:
90 tmpLastWord |= ((uint64_t)bytes[1]) << SIPHASH_ONE_OCTET_TO_BITS;
91 /* fall-through */
92 case 1:
93 tmpLastWord |= ((uint64_t)bytes[0]);
94 /* fall-through */
95 default: // case 0
96 break;
97 }
98 return tmpLastWord;
99 }
100
SiproundOperation(uint64_t * state0,uint64_t * state1,uint64_t * state2,uint64_t * state3)101 static void SiproundOperation(uint64_t *state0, uint64_t *state1, uint64_t *state2, uint64_t *state3)
102 {
103 (*state0) += (*state1);
104 (*state1) = LROT_UINT64(*state1, 13);
105 (*state1) ^= (*state0);
106 (*state0) = LROT_UINT64(*state0, 32);
107 (*state2) += (*state3);
108 (*state3) = LROT_UINT64(*state3, 16);
109 (*state3) ^= (*state2);
110 (*state0) += (*state3);
111 (*state3) = LROT_UINT64(*state3, 21);
112 (*state3) ^= (*state0);
113 (*state2) += (*state1);
114 (*state1) = LROT_UINT64(*state1, 17);
115 (*state1) ^= (*state2);
116 (*state2) = LROT_UINT64(*state2, 32);
117 }
118
UpdateInternalState(uint64_t curWord,CRYPT_SIPHASH_Ctx * ctx,uint16_t rounds)119 static void UpdateInternalState(uint64_t curWord, CRYPT_SIPHASH_Ctx *ctx, uint16_t rounds)
120 {
121 (ctx->state3) ^= curWord;
122 for (uint16_t j = 0; j < rounds; j++) {
123 SiproundOperation(&(ctx->state0), &(ctx->state1), &(ctx->state2), &(ctx->state3));
124 }
125 (ctx->state0) ^= curWord;
126 }
127
CRYPT_SIPHASH_GetMacLen(const CRYPT_SIPHASH_Ctx * ctx,void * val,uint32_t len)128 static int32_t CRYPT_SIPHASH_GetMacLen(const CRYPT_SIPHASH_Ctx *ctx, void *val, uint32_t len)
129 {
130 if (val == NULL || len != sizeof(uint32_t)) {
131 BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
132 return CRYPT_NULL_INPUT;
133 }
134 *(uint32_t *)val = ctx->hashSize;
135 return CRYPT_SUCCESS;
136 }
137
CRYPT_SIPHASH_NewCtx(CRYPT_MAC_AlgId id)138 CRYPT_SIPHASH_Ctx *CRYPT_SIPHASH_NewCtx(CRYPT_MAC_AlgId id)
139 {
140 int32_t ret;
141 EAL_MacMethLookup macMethod;
142 ret = EAL_MacFindMethod(id, &macMethod);
143 if (ret != CRYPT_SUCCESS) {
144 return NULL;
145 }
146 CRYPT_SIPHASH_Ctx *ctx = BSL_SAL_Calloc(1, sizeof(CRYPT_SIPHASH_Ctx));
147 if (ctx == NULL) {
148 BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
149 return NULL;
150 }
151 const EAL_SiphashMethod *method = macMethod.sip;
152
153 uint16_t cRounds = method->compressionRounds;
154 uint16_t dRounds = method->finalizationRounds;
155 // fill compressionRounds and finalizationRounds
156 ctx->compressionRounds = ((cRounds == 0) ? DEFAULT_COMPRESSION_ROUND : cRounds);
157 ctx->finalizationRounds = ((dRounds == 0) ? DEFAULT_FINALIZATION_ROUND : dRounds);
158 ctx->hashSize = method->hashSize;
159 ctx->accInLen = 0;
160 ctx->offset = 0;
161 return ctx;
162 }
163
CRYPT_SIPHASH_Init(CRYPT_SIPHASH_Ctx * ctx,const uint8_t * key,uint32_t keyLen,void * param)164 int32_t CRYPT_SIPHASH_Init(CRYPT_SIPHASH_Ctx *ctx, const uint8_t *key, uint32_t keyLen, void *param)
165 {
166 (void)param;
167 if (ctx == NULL || key == NULL) {
168 BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
169 return CRYPT_NULL_INPUT;
170 }
171 // invalid key size
172 size_t hashSize = ctx->hashSize;
173 if (keyLen != SIPHASH_KEY_SIZE) {
174 BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
175 return CRYPT_INVALID_ARG;
176 }
177 // invalid digest size
178 if (!((hashSize == SIPHASH_MIN_DIGEST_SIZE) || (hashSize == SIPHASH_MAX_DIGEST_SIZE))) {
179 BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
180 return CRYPT_INVALID_ARG;
181 }
182 // split key byte array to two parts: k0, k1
183 uint64_t numKey0 = BytesToUint64LittleEndian(key);
184 uint64_t numKey1 = BytesToUint64LittleEndian(key + SIPHASH_HALF_KEY_SIZE);
185
186 // fill internal state
187 ctx->state0 = numKey0 ^ 0x736f6d6570736575ULL;
188 ctx->state1 = numKey1 ^ 0x646f72616e646f6dULL;
189 ctx->state2 = numKey0 ^ 0x6c7967656e657261ULL;
190 ctx->state3 = numKey1 ^ 0x7465646279746573ULL;
191 if (hashSize == SIPHASH_MAX_DIGEST_SIZE) {
192 ctx->state1 ^= 0xee;
193 }
194 return CRYPT_SUCCESS;
195 }
196
CRYPT_SIPHASH_Update(CRYPT_SIPHASH_Ctx * ctx,const uint8_t * in,uint32_t inlen)197 int32_t CRYPT_SIPHASH_Update(CRYPT_SIPHASH_Ctx *ctx, const uint8_t *in, uint32_t inlen)
198 {
199 if (ctx == NULL || (in == NULL && inlen != 0)) {
200 BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
201 return CRYPT_NULL_INPUT;
202 }
203 if (inlen > UINT32_MAX - ctx->accInLen) {
204 BSL_ERR_PUSH_ERROR(CRYPT_SIPHASH_INPUT_OVERFLOW);
205 return CRYPT_SIPHASH_INPUT_OVERFLOW;
206 }
207 const uint8_t *tmpIn = in;
208 uint32_t tmpInlen = inlen;
209 ctx->accInLen += tmpInlen;
210 uint64_t curWord = 0;
211
212 if (ctx->offset != 0) {
213 size_t emptySpaceLen = SIPHASH_WORD_SIZE - ctx->offset;
214 if (tmpInlen < emptySpaceLen) {
215 (void)memcpy_s(ctx->remainder + (ctx->offset), tmpInlen, tmpIn, tmpInlen);
216 // update offset, emptySpaceLen shrinks
217 ctx->offset += tmpInlen;
218 return CRYPT_SUCCESS;
219 }
220 // fill ctx->remainder[SIPHASH_WORD_SIZE - ctx->offset] to ctx->remainder[SIPHASH_WORD_SIZE - 1] using in
221 (void)memcpy_s(ctx->remainder + (ctx->offset), emptySpaceLen, tmpIn, emptySpaceLen);
222 // update inlen
223 tmpInlen -= (uint32_t)emptySpaceLen;
224 // consume emptySpaceLen data of in
225 tmpIn += emptySpaceLen;
226 curWord = BytesToUint64LittleEndian(ctx->remainder);
227 (void)UpdateInternalState(curWord, ctx, ctx->compressionRounds);
228 }
229
230 size_t remainLen = tmpInlen & (SIPHASH_WORD_SIZE - 1); // inlen mod 8
231 const uint8_t *lastWordPos = tmpIn + tmpInlen - remainLen;
232 while (tmpIn != lastWordPos) {
233 curWord = BytesToUint64LittleEndian(tmpIn);
234 (void)UpdateInternalState(curWord, ctx, ctx->compressionRounds);
235 tmpIn += SIPHASH_WORD_SIZE;
236 }
237 if (remainLen > 0) {
238 (void)memcpy_s(ctx->remainder, remainLen, lastWordPos, remainLen);
239 }
240 ctx->offset = (uint32_t)remainLen;
241 return CRYPT_SUCCESS;
242 }
243
CRYPT_SIPHASH_Final(CRYPT_SIPHASH_Ctx * ctx,uint8_t * out,uint32_t * outlen)244 int32_t CRYPT_SIPHASH_Final(CRYPT_SIPHASH_Ctx *ctx, uint8_t *out, uint32_t *outlen)
245 {
246 if (ctx == NULL || out == NULL || outlen == NULL) {
247 BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
248 return CRYPT_NULL_INPUT;
249 }
250
251 if (*outlen < ctx->hashSize) {
252 BSL_ERR_PUSH_ERROR(CRYPT_SIPHASH_OUT_BUFF_LEN_NOT_ENOUGH);
253 return CRYPT_SIPHASH_OUT_BUFF_LEN_NOT_ENOUGH;
254 }
255 *outlen = ctx->hashSize;
256
257 uint64_t mLen = ctx->accInLen; // message length
258 uint64_t tmpLastWord = mLen << 56; // put (mLen mod 256) at high address
259 size_t remainLen = ctx->offset;
260 uint64_t curWord = DealLastWord(tmpLastWord, ctx->remainder, remainLen);
261 (void)UpdateInternalState(curWord, ctx, ctx->compressionRounds);
262
263 if (*outlen == SIPHASH_MIN_DIGEST_SIZE) {
264 (ctx->state2) ^= 0xff;
265 } else {
266 (ctx->state2) ^= 0xee;
267 }
268 for (uint16_t j = 0; j < ctx->finalizationRounds; j++) {
269 (void)SiproundOperation(&(ctx->state0), &(ctx->state1), &(ctx->state2), &(ctx->state3));
270 }
271 uint64_t state = (ctx->state0) ^ (ctx->state1) ^ (ctx->state2) ^ (ctx->state3);
272 (void)Uint64ToBytesLittleEndian(state, out);
273 if (*outlen == SIPHASH_MIN_DIGEST_SIZE) {
274 return CRYPT_SUCCESS;
275 }
276 (ctx->state1) ^= 0xdd;
277 for (uint16_t j = 0; j < ctx->finalizationRounds; j++) {
278 (void)SiproundOperation(&(ctx->state0), &(ctx->state1), &(ctx->state2), &(ctx->state3));
279 }
280 state = (ctx->state0) ^ (ctx->state1) ^ (ctx->state2) ^ (ctx->state3);
281 (void)Uint64ToBytesLittleEndian(state, out + SIPHASH_WORD_SIZE);
282 return CRYPT_SUCCESS;
283 }
284
CRYPT_SIPHASH_Reinit(CRYPT_SIPHASH_Ctx * ctx)285 void CRYPT_SIPHASH_Reinit(CRYPT_SIPHASH_Ctx *ctx)
286 {
287 if (ctx == NULL) {
288 BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
289 return;
290 }
291 ctx->state0 = 0;
292 ctx->state1 = 0;
293 ctx->state2 = 0;
294 ctx->state3 = 0;
295 ctx->accInLen = 0;
296 ctx->offset = 0;
297 (void)memset_s(ctx->remainder, SIPHASH_WORD_SIZE, 0, SIPHASH_WORD_SIZE);
298 }
299
CRYPT_SIPHASH_Deinit(CRYPT_SIPHASH_Ctx * ctx)300 void CRYPT_SIPHASH_Deinit(CRYPT_SIPHASH_Ctx *ctx)
301 {
302 if (ctx == NULL) {
303 BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
304 return;
305 }
306 }
307
CRYPT_SIPHASH_Ctrl(CRYPT_SIPHASH_Ctx * ctx,uint32_t opt,void * val,uint32_t len)308 int32_t CRYPT_SIPHASH_Ctrl(CRYPT_SIPHASH_Ctx *ctx, uint32_t opt, void *val, uint32_t len)
309 {
310 if (ctx == NULL) {
311 BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
312 return CRYPT_NULL_INPUT;
313 }
314 switch (opt) {
315 case CRYPT_CTRL_GET_MACLEN:
316 return CRYPT_SIPHASH_GetMacLen(ctx, val, len);
317 default:
318 break;
319 }
320 BSL_ERR_PUSH_ERROR(CRYPT_SIPHASH_ERR_UNSUPPORTED_CTRL_OPTION);
321 return CRYPT_SIPHASH_ERR_UNSUPPORTED_CTRL_OPTION;
322 }
323
CRYPT_SIPHASH_FreeCtx(CRYPT_SIPHASH_Ctx * ctx)324 void CRYPT_SIPHASH_FreeCtx(CRYPT_SIPHASH_Ctx *ctx)
325 {
326 if (ctx != NULL) {
327 BSL_SAL_Free(ctx);
328 }
329 }
330 #endif /* HITLS_CRYPTO_SIPHASH */
331