• 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_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