Coverage Report

Created: 2025-07-23 06:59

/src/wolfssl-sp-math-all/wolfcrypt/src/siphash.c
Line
Count
Source (jump to first uncovered line)
1
/* siphash.c
2
 *
3
 * Copyright (C) 2006-2025 wolfSSL Inc.
4
 *
5
 * This file is part of wolfSSL.
6
 *
7
 * wolfSSL is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * wolfSSL is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
20
 */
21
22
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
23
24
#if defined(WC_SIPHASH_NO_ASM) && !defined(WOLFSSL_NO_ASM)
25
    #define WOLFSSL_NO_ASM
26
#endif
27
28
#include <wolfssl/wolfcrypt/siphash.h>
29
30
#ifdef NO_INLINE
31
    #include <wolfssl/wolfcrypt/misc.h>
32
#else
33
    #define WOLFSSL_MISC_INCLUDED
34
    #include <wolfcrypt/src/misc.c>
35
#endif
36
37
38
/* DESCRIPTION
39
 *
40
 * SipHash is a PseudoRandom Function (PRF) that can be used with small
41
 * messages (less than 256 bytes).
42
 * SipHash can be used for Message Authentication Codes (MACs) and as such must
43
 * be passed a secret key.
44
 * https://eprint.iacr.org/2012/351.pdf
45
 *
46
 * SipHash is commonly used in hash tables.
47
 * Do not use this as a hash not as a general purpose MAC.
48
 *
49
 * WOLFSSL_SIPHASH_CROUNDS and WOLFSSL_SIPHASH_DROUNDS can be defined at build
50
 * time to change the algorithm.
51
 * Default is SipHash-2-4:
52
 *   WOLFSSL_SIPHASH_CROUNDS = 2
53
 *   WOLFSSL_SIPHASH_DROUNDS = 4
54
 *
55
 * Inline assembly implementations of wc_SipHash() written for:
56
 *   - GCC for Intel x86_64
57
 *   - GCC for Aarch64.
58
 */
59
60
#ifdef WOLFSSL_SIPHASH
61
62
#ifdef LITTLE_ENDIAN_ORDER
63
/**
64
 * Decode little-endian byte array to 64-bit number.
65
 *
66
 * @param [in] a  Little-endian byte array.
67
 * @return 64-bit number.
68
 */
69
0
#define GET_U64(a)      readUnalignedWord64(a)
70
/**
71
 * Decode little-endian byte array to 32-bit number.
72
 *
73
 * @param [in] a  Little-endian byte array.
74
 * @return 32-bit number.
75
 */
76
#define GET_U32(a)      readUnalignedWord32(a)
77
/**
78
 * Decode little-endian byte array to 16-bit number.
79
 *
80
 * @param [in] a  Little-endian byte array.
81
 * @return 16-bit number.
82
 */
83
#define GET_U16(a)      (*(word16*)(a))
84
/**
85
 * Encode 64-bit number to a little-endian byte array.
86
 *
87
 * @param [out] a  Byte array to write into.
88
 * @param [in]  n  Number to encode.
89
 */
90
0
#define SET_U64(a, n)   writeUnalignedWord64(a, n)
91
#else
92
/**
93
 * Decode little-endian byte array to 64-bit number.
94
 *
95
 * @param [in] a  Little-endian byte array.
96
 * @return 64-bit number.
97
 */
98
#define GET_U64(a)      (((word64)((a)[7]) << 56) |     \
99
                         ((word64)((a)[6]) << 48) |     \
100
                         ((word64)((a)[5]) << 40) |     \
101
                         ((word64)((a)[4]) << 32) |     \
102
                         ((word64)((a)[3]) << 24) |     \
103
                         ((word64)((a)[2]) << 16) |     \
104
                         ((word64)((a)[1]) <<  8) |     \
105
                         ((word64)((a)[0])      ))
106
/**
107
 * Decode little-endian byte array to 32-bit number.
108
 *
109
 * @param [in] a  Little-endian byte array.
110
 * @return 32-bit number.
111
 */
112
#define GET_U32(a)      (((word32)((a)[3]) << 24) |     \
113
                         ((word32)((a)[2]) << 16) |     \
114
                         ((word32)((a)[1]) <<  8) |     \
115
                         ((word32)((a)[0])      ))
116
/**
117
 * Decode little-endian byte array to 16-bit number.
118
 *
119
 * @param [in] a  Little-endian byte array.
120
 * @return 16-bit number.
121
 */
122
#define GET_U16(a)      (((word16)((a)[1]) <<  8) |     \
123
                         ((word16)((a)[0])      ))
124
/**
125
 * Encode 64-bit number to a little-endian byte array.
126
 *
127
 * @param [out] a  Byte array to write into.
128
 * @param [in]  n  Number to encode.
129
 */
130
#define SET_U64(a, n)   (a)[0] = (byte)((n)      );     \
131
                        (a)[1] = (byte)((n) >>  8);     \
132
                        (a)[2] = (byte)((n) >> 16);     \
133
                        (a)[3] = (byte)((n) >> 24);     \
134
                        (a)[4] = (byte)((n) >> 32);     \
135
                        (a)[5] = (byte)((n) >> 40);     \
136
                        (a)[6] = (byte)((n) >> 48);     \
137
                        (a)[7] = (byte)((n) >> 56)
138
#endif
139
140
/**
141
 * Initialize SipHash operation with a key.
142
 *
143
 * @param [out] sipHash  SipHash object.
144
 * @param [in]  key      16 byte array - little endian.
145
 * @return  BAD_FUNC_ARG when sipHash or key is NULL.
146
 * @return  BAD_FUNC_ARG when outSz is neither 8 nor 16.
147
 * @return  0 on success.
148
 */
149
int wc_InitSipHash(SipHash* sipHash, const unsigned char* key,
150
    unsigned char outSz)
151
0
{
152
0
    int ret = 0;
153
154
    /* Validate parameters. */
155
0
    if ((sipHash == NULL) || (key == NULL) ||
156
0
        ((outSz != SIPHASH_MAC_SIZE_8) && (outSz != SIPHASH_MAC_SIZE_16))) {
157
0
        ret = BAD_FUNC_ARG;
158
0
    }
159
160
0
    if (ret == 0) {
161
0
        word64 k0 = GET_U64(key + 0);
162
0
        word64 k1 = GET_U64(key + 8);
163
164
        /* Initialize state with key. */
165
0
        sipHash->v[0] = W64LIT(0x736f6d6570736575);
166
0
        if (outSz == SIPHASH_MAC_SIZE_8) {
167
0
            sipHash->v[1] = W64LIT(0x646f72616e646f6d);
168
0
        }
169
0
        else {
170
0
            sipHash->v[1] = W64LIT(0x646f72616e646f83);
171
0
        }
172
0
        sipHash->v[2] = W64LIT(0x6c7967656e657261);
173
0
        sipHash->v[3] = W64LIT(0x7465646279746573);
174
175
0
        sipHash->v[0] ^= k0;
176
0
        sipHash->v[1] ^= k1;
177
0
        sipHash->v[2] ^= k0;
178
0
        sipHash->v[3] ^= k1;
179
180
        /* No cached message bytes. */
181
0
        sipHash->cacheCnt = 0;
182
        /* No message bytes compressed yet. */
183
0
        sipHash->inCnt = 0;
184
        /* Keep the output size to check against final call. */
185
0
        sipHash->outSz = outSz;
186
0
    }
187
188
0
    return ret;
189
0
}
190
191
/**
192
 * One round of SipHash.
193
 *
194
 * @param [in, out] sipHash  SipHash object.
195
 */
196
static WC_INLINE void SipRound(SipHash *sipHash)
197
0
{
198
0
    word64* v = sipHash->v;
199
200
0
    v[0] += v[1];
201
0
    v[2] += v[3];
202
0
    v[1] = rotlFixed64(v[1], 13);
203
0
    v[3] = rotlFixed64(v[3], 16);
204
0
    v[1] ^= v[0];
205
0
    v[3] ^= v[2];
206
0
    v[0] = rotlFixed64(v[0], 32);
207
0
    v[2] += v[1];
208
0
    v[0] += v[3];
209
0
    v[1] = rotlFixed64(v[1], 17);
210
0
    v[3] = rotlFixed64(v[3], 21);
211
0
    v[1] ^= v[2];
212
0
    v[3] ^= v[0];
213
0
    v[2] = rotlFixed64(v[2], 32);
214
0
}
215
216
/**
217
 * One step of the compression operation.
218
 *
219
 * @param [in, out] sipHash  SipHash object.
220
 * @param [in]      m        Message to compress.
221
 */
222
static WC_INLINE void SipHashCompress(SipHash* sipHash, const byte* m)
223
0
{
224
0
    int i;
225
226
0
    sipHash->v[3] ^= GET_U64(m);
227
0
    for (i = 0; i < WOLFSSL_SIPHASH_CROUNDS; i++) {
228
0
        SipRound(sipHash);
229
0
    }
230
0
    sipHash->v[0] ^= GET_U64(m);
231
0
}
232
233
/**
234
 * Update the SipHash operation with more data.
235
 *
236
 * @param [in, out] sipHash  SipHash object.
237
 * @param [in]      in       Input message.
238
 * @param [in]      inSz     Size of input message.
239
 * @return  BAD_FUNC_ARG when sipHash is NULL.
240
 * @return  BAD_FUNC_ARG when in is NULL and inSz is not zero.
241
 * @return  0 on success.
242
 */
243
int wc_SipHashUpdate(SipHash* sipHash, const unsigned char* in, word32 inSz)
244
0
{
245
0
    int ret = 0;
246
247
    /* Validate parameters. */
248
0
    if ((sipHash == NULL) || ((in == NULL) && (inSz != 0))) {
249
0
        ret = BAD_FUNC_ARG;
250
0
    }
251
252
    /* Process any message bytes. */
253
0
    if ((ret == 0) && (inSz > 0)) {
254
        /* Add to cache if already started. */
255
0
        if (sipHash->cacheCnt > 0) {
256
0
            byte len = (byte)(SIPHASH_BLOCK_SIZE - sipHash->cacheCnt);
257
0
            if (len > inSz) {
258
0
                len = (byte)inSz;
259
0
            }
260
0
            XMEMCPY(sipHash->cache + sipHash->cacheCnt, in, len);
261
0
            in += len;
262
0
            inSz -= len;
263
0
            sipHash->cacheCnt = (byte)(sipHash->cacheCnt + len);
264
265
0
            if (sipHash->cacheCnt == SIPHASH_BLOCK_SIZE) {
266
                /* Compress the block from the cache. */
267
0
                SipHashCompress(sipHash, sipHash->cache);
268
0
                sipHash->inCnt += SIPHASH_BLOCK_SIZE;
269
0
                sipHash->cacheCnt = 0;
270
0
            }
271
0
        }
272
273
        /* Process more blocks from message. */
274
0
        while (inSz >= SIPHASH_BLOCK_SIZE) {
275
            /* Compress the next block from the message data. */
276
0
            SipHashCompress(sipHash, in);
277
0
            in += SIPHASH_BLOCK_SIZE;
278
0
            inSz -= SIPHASH_BLOCK_SIZE;
279
0
            sipHash->inCnt += SIPHASH_BLOCK_SIZE;
280
0
        }
281
282
0
        if (inSz > 0) {
283
            /* Cache remaining message bytes less than a block. */
284
0
            XMEMCPY(sipHash->cache, in, inSz);
285
0
            sipHash->cacheCnt = (byte)inSz;
286
0
        }
287
0
    }
288
289
0
    return ret;
290
0
}
291
292
/**
293
 * Calculate 8-bytes of output.
294
 *
295
 * @param [in, out] sipHash  SipHash object.
296
 * @param [out]     out      Buffer to place 8-bytes of MAC into.
297
 */
298
static WC_INLINE void SipHashOut(SipHash* sipHash, byte* out)
299
0
{
300
0
    word64 n;
301
0
    int i;
302
303
0
    for (i = 0; i < WOLFSSL_SIPHASH_DROUNDS; i++) {
304
0
        SipRound(sipHash);
305
0
    }
306
0
    n = sipHash->v[0] ^ sipHash->v[1] ^ sipHash->v[2] ^ sipHash->v[3];
307
0
    SET_U64(out, n);
308
0
}
309
310
/**
311
 * Finalize SipHash operation.
312
 *
313
 * @param [in, out] sipHash  SipHash object.
314
 * @param [out]     out      Buffer to place MAC into.
315
 * @param [in]      outSz    Size of output MAC. 8 or 16 only.
316
 * @return  BAD_FUNC_ARG when sipHash or out is NULL.
317
 * @return  BAD_FUNC_ARG when outSz is not the same as initialized value.
318
 * @return  0 on success.
319
 */
320
int wc_SipHashFinal(SipHash* sipHash, unsigned char* out, unsigned char outSz)
321
0
{
322
0
    int ret = 0;
323
324
    /* Validate parameters. */
325
0
    if ((sipHash == NULL) || (out == NULL) || (outSz != sipHash->outSz)) {
326
0
        ret = BAD_FUNC_ARG;
327
0
    }
328
329
0
    if (ret == 0) {
330
        /* Put in remaining cached message bytes. */
331
0
        XMEMSET(sipHash->cache + sipHash->cacheCnt, 0, 7U - sipHash->cacheCnt);
332
0
        sipHash->cache[7] = (byte)(sipHash->inCnt + sipHash->cacheCnt);
333
334
0
        SipHashCompress(sipHash, sipHash->cache);
335
0
        sipHash->cacheCnt = 0;
336
337
        /* Output either 8 or 16 bytes. */
338
0
        if (outSz == SIPHASH_MAC_SIZE_8) {
339
0
            sipHash->v[2] ^= (word64)0xff;
340
0
            SipHashOut(sipHash, out);
341
0
        }
342
0
        else {
343
0
            sipHash->v[2] ^= (word64)0xee;
344
0
            SipHashOut(sipHash, out);
345
0
            sipHash->v[1] ^= (word64)0xdd;
346
0
            SipHashOut(sipHash, out + 8);
347
0
        }
348
0
    }
349
350
0
    return ret;
351
0
}
352
353
#if !defined(WOLFSSL_NO_ASM) && defined(__GNUC__) && defined(__x86_64__) && \
354
    (WOLFSSL_SIPHASH_CROUNDS == 1 || WOLFSSL_SIPHASH_CROUNDS == 2) && \
355
    (WOLFSSL_SIPHASH_DROUNDS == 2 || WOLFSSL_SIPHASH_DROUNDS == 4)
356
357
#define SIPHASH_ROUND(v0, v1, v2, v3)   \
358
        "addq   " #v1 ", " #v0 "\n\t"   \
359
        "addq   " #v3 ", " #v2 "\n\t"   \
360
        "rolq   $13, " #v1 "\n\t"       \
361
        "rolq   $16, " #v3 "\n\t"       \
362
        "xorq   " #v0 ", " #v1 "\n\t"   \
363
        "xorq   " #v2 ", " #v3 "\n\t"   \
364
        "rolq   $32, " #v0 "\n\t"       \
365
        "addq   " #v1 ", " #v2 "\n\t"   \
366
        "addq   " #v3 ", " #v0 "\n\t"   \
367
        "rolq   $17, " #v1 "\n\t"       \
368
        "rolq   $21, " #v3 "\n\t"       \
369
        "xorq   " #v2 ", " #v1 "\n\t"   \
370
        "xorq   " #v0 ", " #v3 "\n\t"   \
371
        "rolq   $32, " #v2 "\n\t"
372
373
#define SIPHASH_LAST_ROUND(v0, v1, v2, v3)  \
374
        "addq   " #v1 ", " #v0 "\n\t"       \
375
        "addq   " #v3 ", " #v2 "\n\t"       \
376
        "rolq   $13, " #v1 "\n\t"           \
377
        "rolq   $16, " #v3 "\n\t"           \
378
        "xorq   " #v0 ", " #v1 "\n\t"       \
379
        "xorq   " #v2 ", " #v3 "\n\t"       \
380
        "addq   " #v1 ", " #v2 "\n\t"       \
381
        "rolq   $17, " #v1 "\n\t"           \
382
        "rolq   $21, " #v3 "\n\t"           \
383
        "xorq   " #v2 ", " #v1 "\n\t"       \
384
        "rolq   $32, " #v2 "\n\t"
385
386
/**
387
 * Perform SipHash operation on input with key.
388
 *
389
 * @param [in]      key      16 byte array - little endian.
390
 * @param [in]      in       Input message.
391
 * @param [in]      inSz     Size of input message.
392
 * @param [out]     out      Buffer to place MAC into.
393
 * @param [in]      outSz    Size of output MAC. 8 or 16 only.
394
 * @return  BAD_FUNC_ARG when key or out is NULL.
395
 * @return  BAD_FUNC_ARG when in is NULL and inSz is not zero.
396
 * @return  BAD_FUNC_ARG when outSz is neither 8 nor 16.
397
 * @return  0 on success.
398
 */
399
int wc_SipHash(const unsigned char* key, const unsigned char* in, word32 inSz,
400
    unsigned char* out, unsigned char outSz)
401
0
{
402
0
    word64 v0 = 0x736f6d6570736575L;
403
0
    word64 v1 = 0x646f72616e646f6dL;
404
0
    word64 v2 = 0x6c7967656e657261L;
405
0
    word64 v3 = 0x7465646279746573L;
406
0
    word64 k0;
407
0
    word64 k1;
408
409
0
    if ((key == NULL) || ((in == NULL) && (inSz != 0)) || (out == NULL) ||
410
0
            ((outSz != SIPHASH_MAC_SIZE_8) && (outSz != SIPHASH_MAC_SIZE_16))) {
411
0
        return BAD_FUNC_ARG;
412
0
    }
413
414
0
    k0 = ((word64*)key)[0];
415
0
    k1 = ((word64*)key)[1];
416
0
    __asm__ __volatile__ (
417
0
        "xorq   %[k0], %[v0]\n\t"
418
0
        "xorq   %[k1], %[v1]\n\t"
419
0
        "xorq   %[k0], %[v2]\n\t"
420
0
        "xorq   %[k1], %[v3]\n\t"
421
422
0
        "cmp    $8, %[outSz]\n\t"
423
0
        "mov    %[inSz], %k[k1]\n\t"
424
0
        "je     L_siphash_8_top\n\t"
425
0
        "xorq   $0xee, %[v1]\n\t"
426
0
        "L_siphash_8_top:\n\t"
427
428
0
        "sub    $8, %[inSz]\n\t"
429
0
        "jb     L_siphash_done_input_8\n\t"
430
0
        "L_siphash_input:\n\t"
431
0
        "movq   (%[in]), %[k0]\n\t"
432
0
        "addq   $8, %[in]\n\t"
433
0
        "xorq   %[k0], %[v3]\n\t"
434
#if WOLFSSL_SIPHASH_CROUNDS == 1
435
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
436
#elif WOLFSSL_SIPHASH_CROUNDS == 2
437
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
438
0
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
439
0
#endif
440
0
        "xorq   %[k0], %[v0]\n\t"
441
0
        "sub    $8, %[inSz]\n\t"
442
0
        "jge    L_siphash_input\n\t"
443
0
        "L_siphash_done_input_8:\n\t"
444
0
        "add    $8, %[inSz]\n\t"
445
446
0
        "shlq   $56, %[k1]\n\t"
447
0
        "cmp    $0, %[inSz]\n\t"
448
0
        "je     L_siphash_last_done\n\t"
449
0
        "cmp    $4, %[inSz]\n\t"
450
0
        "jl     L_siphash_last_lt4\n\t"
451
452
0
        "cmp    $7, %[inSz]\n\t"
453
0
        "jl     L_siphash_n7\n\t"
454
0
        "movzbq 6(%[in]), %[k0]\n\t"
455
0
        "shlq   $48, %[k0]\n\t"
456
0
        "orq    %[k0], %[k1]\n\t"
457
0
        "L_siphash_n7:\n\t"
458
459
0
        "cmp    $6, %[inSz]\n\t"
460
0
        "jl     L_siphash_n6\n\t"
461
0
        "movzbq 5(%[in]), %[k0]\n\t"
462
0
        "shlq   $40, %[k0]\n\t"
463
0
        "orq    %[k0], %[k1]\n\t"
464
0
        "L_siphash_n6:\n\t"
465
466
0
        : [in] "+r" (in), [inSz] "+r" (inSz), [k0] "+r" (k0), [k1] "+r" (k1),
467
0
          [v0] "+r" (v0), [v1] "+r" (v1), [v2] "+r" (v2), [v3] "+r" (v3)
468
0
        : [out] "r" (out) , [outSz] "r" (outSz)
469
0
        : "memory"
470
0
    );
471
472
0
    __asm__ __volatile__ (
473
0
        "cmp    $5, %[inSz]\n\t"
474
0
        "jl     L_siphash_n5\n\t"
475
0
        "movzbq 4(%[in]), %[k0]\n\t"
476
0
        "shlq   $32, %[k0]\n\t"
477
0
        "orq    %[k0], %[k1]\n\t"
478
0
        "L_siphash_n5:\n\t"
479
480
0
        "mov    (%[in]), %k[k0]\n\t"
481
0
        "orq    %[k0], %[k1]\n\t"
482
0
        "jmp    L_siphash_last_done\n\t"
483
484
0
        "L_siphash_last_lt4:\n\t"
485
486
0
        "cmp    $1, %[inSz]\n\t"
487
0
        "je     L_siphash_last_1\n\t"
488
489
0
        "cmp    $3, %[inSz]\n\t"
490
0
        "jl     L_siphash_n3\n\t"
491
0
        "movzbq 2(%[in]), %[k0]\n\t"
492
0
        "shlq   $16, %[k0]\n\t"
493
0
        "orq    %[k0], %[k1]\n\t"
494
0
        "L_siphash_n3:\n\t"
495
496
0
        "movw   (%[in]), %w[k0]\n\t"
497
0
        "or     %w[k0], %w[k1]\n\t"
498
0
        "jmp    L_siphash_last_done\n\t"
499
500
0
        "L_siphash_last_1:\n\t"
501
0
        "movb   (%[in]), %b[k0]\n\t"
502
0
        "or     %b[k0], %b[k1]\n\t"
503
504
0
        "L_siphash_last_done:\n\t"
505
506
0
        "xorq   %[k1], %[v3]\n\t"
507
#if WOLFSSL_SIPHASH_CROUNDS == 1
508
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
509
#elif WOLFSSL_SIPHASH_CROUNDS == 2
510
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
511
0
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
512
0
#endif
513
0
        "xorq   %[k1], %[v0]\n\t"
514
515
0
        : [in] "+r" (in), [inSz] "+r" (inSz), [k0] "+r" (k0), [k1] "+r" (k1),
516
0
          [v0] "+r" (v0), [v1] "+r" (v1), [v2] "+r" (v2), [v3] "+r" (v3)
517
0
        : [out] "r" (out) , [outSz] "r" (outSz)
518
0
        : "memory"
519
0
    );
520
521
0
    __asm__ __volatile__ (
522
0
        "cmp    $8, %[outSz]\n\t"
523
0
        "je     L_siphash_8_end\n\t"
524
525
0
        "xor    $0xee, %b[v2]\n\t"
526
#if WOLFSSL_SIPHASH_DROUNDS == 2
527
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
528
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
529
#elif WOLFSSL_SIPHASH_DROUNDS == 4
530
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
531
0
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
532
0
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
533
0
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
534
0
#endif
535
0
        "movq   %[v0], %[k0]\n\t"
536
0
        "xorq   %[v1], %[k0]\n\t"
537
0
        "xorq   %[v2], %[k0]\n\t"
538
0
        "xorq   %[v3], %[k0]\n\t"
539
0
        "movq   %[k0], (%[out])\n\t"
540
541
0
        "xor    $0xdd, %b[v1]\n\t"
542
#if WOLFSSL_SIPHASH_DROUNDS == 2
543
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
544
        SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
545
#elif WOLFSSL_SIPHASH_DROUNDS == 4
546
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
547
0
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
548
0
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
549
0
        SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
550
0
#endif
551
0
        "xorq   %[v3], %[v1]\n\t"
552
0
        "xorq   %[v2], %[v1]\n\t"
553
0
        "movq   %[v1], 8(%[out])\n\t"
554
0
        "jmp    L_siphash_done\n\t"
555
556
0
        "L_siphash_8_end:\n\t"
557
0
        "xor    $0xff, %b[v2]\n\t"
558
#if WOLFSSL_SIPHASH_DROUNDS == 2
559
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
560
        SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
561
#elif WOLFSSL_SIPHASH_DROUNDS == 4
562
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
563
0
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
564
0
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
565
0
        SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
566
0
#endif
567
0
        "xorq   %[v3], %[v1]\n\t"
568
0
        "xorq   %[v2], %[v1]\n\t"
569
0
        "movq   %[v1], (%[out])\n\t"
570
571
0
        "L_siphash_done:\n\t"
572
573
0
        : [in] "+r" (in), [inSz] "+r" (inSz), [k0] "+r" (k0), [k1] "+r" (k1),
574
0
          [v0] "+r" (v0), [v1] "+r" (v1), [v2] "+r" (v2), [v3] "+r" (v3)
575
0
        : [out] "r" (out) , [outSz] "r" (outSz)
576
0
        : "memory"
577
0
    );
578
579
0
    return 0;
580
0
}
581
582
#elif defined(WOLFSSL_ARMASM) && defined(__GNUC__) && defined(__aarch64__) && \
583
    (WOLFSSL_SIPHASH_CROUNDS == 1 || WOLFSSL_SIPHASH_CROUNDS == 2) && \
584
    (WOLFSSL_SIPHASH_DROUNDS == 2 || WOLFSSL_SIPHASH_DROUNDS == 4)
585
586
#define SIPHASH_ROUND(v0, v1, v2, v3)            \
587
        "add    " #v0 ", " #v0 ", " #v1 "\n\t"   \
588
        "add    " #v2 ", " #v2 ", " #v3 "\n\t"   \
589
        "ror    " #v1 ", " #v1 ", #51\n\t"       \
590
        "ror    " #v3 ", " #v3 ", #48\n\t"       \
591
        "eor    " #v1 ", " #v1 ", " #v0 "\n\t"   \
592
        "eor    " #v3 ", " #v3 ", " #v2 "\n\t"   \
593
        "ror    " #v0 ", " #v0 ", #32\n\t"       \
594
        "add    " #v2 ", " #v2 ", " #v1 "\n\t"   \
595
        "add    " #v0 ", " #v0 ", " #v3 "\n\t"   \
596
        "ror    " #v1 ", " #v1 ", #47\n\t"       \
597
        "ror    " #v3 ", " #v3 ", #43\n\t"       \
598
        "eor    " #v1 ", " #v1 ", " #v2 "\n\t"   \
599
        "eor    " #v3 ", " #v3 ", " #v0 "\n\t"   \
600
        "ror    " #v2 ", " #v2 ", #32\n\t"
601
602
#define SIPHASH_LAST_ROUND(v0, v1, v2, v3)       \
603
        "add    " #v0 ", " #v0 ", " #v1 "\n\t"   \
604
        "add    " #v2 ", " #v2 ", " #v3 "\n\t"   \
605
        "ror    " #v1 ", " #v1 ", #51\n\t"       \
606
        "ror    " #v3 ", " #v3 ", #48\n\t"       \
607
        "eor    " #v1 ", " #v1 ", " #v0 "\n\t"   \
608
        "eor    " #v3 ", " #v3 ", " #v2 "\n\t"   \
609
        "add    " #v2 ", " #v2 ", " #v1 "\n\t"   \
610
        "ror    " #v1 ", " #v1 ", #47\n\t"       \
611
        "ror    " #v3 ", " #v3 ", #43\n\t"       \
612
        "eor    " #v1 ", " #v1 ", " #v2 "\n\t"   \
613
        "ror    " #v2 ", " #v2 ", #32\n\t"
614
615
/**
616
 * Perform SipHash operation on input with key.
617
 *
618
 * @param [in]      key      16 byte array - little endian.
619
 * @param [in]      in       Input message.
620
 * @param [in]      inSz     Size of input message.
621
 * @param [out]     out      Buffer to place MAC into.
622
 * @param [in]      outSz    Size of output MAC. 8 or 16 only.
623
 * @return  BAD_FUNC_ARG when key or out is NULL.
624
 * @return  BAD_FUNC_ARG when in is NULL and inSz is not zero.
625
 * @return  BAD_FUNC_ARG when outSz is not 8 nor 16.
626
 * @return  0 on success.
627
 */
628
int wc_SipHash(const unsigned char* key, const unsigned char* in, word32 inSz,
629
    unsigned char* out, unsigned char outSz)
630
{
631
    word64 v0 = 0x736f6d6570736575L;
632
    word64 v1 = 0x646f72616e646f6dL;
633
    word64 v2 = 0x6c7967656e657261L;
634
    word64 v3 = 0x7465646279746573L;
635
    word64 k0;
636
    word64 k1;
637
638
    if ((key == NULL) || ((in == NULL) && (inSz != 0)) || (out == NULL) ||
639
            ((outSz != SIPHASH_MAC_SIZE_8) && (outSz != SIPHASH_MAC_SIZE_16))) {
640
        return BAD_FUNC_ARG;
641
    }
642
643
    k0 = ((word64*)key)[0];
644
    k1 = ((word64*)key)[1];
645
    __asm__ __volatile__ (
646
        "eor    %[v0], %[v0], %[k0]\n\t"
647
        "eor    %[v1], %[v1], %[k1]\n\t"
648
        "eor    %[v2], %[v2], %[k0]\n\t"
649
        "eor    %[v3], %[v3], %[k1]\n\t"
650
651
        "mov    %w[k1], %w[inSz]\n\t"
652
        "cmp    %w[outSz], #8\n\t"
653
        "b.eq   L_siphash_8_top\n\t"
654
        "mov    %w[k0], #0xee\n\t"
655
        "eor    %[v1], %[v1], %[k0]\n\t"
656
        "L_siphash_8_top:\n\t"
657
658
        "subs   %w[inSz], %w[inSz], #8\n\t"
659
        "b.mi   L_siphash_done_input_8\n\t"
660
        "L_siphash_input:\n\t"
661
        "ldr    %[k0], [%[in]], #8\n\t"
662
        "eor    %[v3], %[v3], %[k0]\n\t"
663
#if WOLFSSL_SIPHASH_CROUNDS == 1
664
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
665
#elif WOLFSSL_SIPHASH_CROUNDS == 2
666
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
667
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
668
#endif
669
        "eor    %[v0], %[v0], %[k0]\n\t"
670
        "subs   %w[inSz], %w[inSz], #8\n\t"
671
        "b.ge   L_siphash_input\n\t"
672
        "L_siphash_done_input_8:\n\t"
673
        "add    %w[inSz], %w[inSz], #8\n\t"
674
675
        "lsl    %[k1], %[k1], #56\n\t"
676
        "cmp    %w[inSz], #0\n\t"
677
        "b.eq   L_siphash_last_done\n\t"
678
        "cmp    %w[inSz], #4\n\t"
679
        "b.lt   L_siphash_last_lt4\n\t"
680
681
        "cmp    %w[inSz], #7\n\t"
682
        "b.lt   L_siphash_n7\n\t"
683
        "ldrb   %w[k0], [%[in], 6]\n\t"
684
        "orr    %[k1], %[k1], %[k0], lsl 48\n\t"
685
        "L_siphash_n7:\n\t"
686
687
        "cmp    %w[inSz], #6\n\t"
688
        "b.lt   L_siphash_n6\n\t"
689
        "ldrb   %w[k0], [%[in], 5]\n\t"
690
        "orr    %[k1], %[k1], %[k0], lsl 40\n\t"
691
        "L_siphash_n6:\n\t"
692
693
        "cmp    %w[inSz], #5\n\t"
694
        "b.lt   L_siphash_n5\n\t"
695
        "ldrb   %w[k0], [%[in], 4]\n\t"
696
        "orr    %[k1], %[k1], %[k0], lsl 32\n\t"
697
        "L_siphash_n5:\n\t"
698
699
        "ldr    %w[k0], [%[in]]\n\t"
700
        "orr    %[k1], %[k1], %[k0]\n\t"
701
        "b      L_siphash_last_done\n\t"
702
703
        "L_siphash_last_lt4:\n\t"
704
705
        "cmp    %w[inSz], #1\n\t"
706
        "b.eq   L_siphash_last_1\n\t"
707
708
        "cmp    %w[inSz], #3\n\t"
709
        "b.lt   L_siphash_n3\n\t"
710
        "ldrb   %w[k0], [%[in], 2]\n\t"
711
        "orr    %[k1], %[k1], %[k0], lsl 16\n\t"
712
        "L_siphash_n3:\n\t"
713
714
        "ldrh   %w[k0], [%[in]]\n\t"
715
        "orr    %[k1], %[k1], %[k0]\n\t"
716
        "b      L_siphash_last_done\n\t"
717
718
        "L_siphash_last_1:\n\t"
719
        "ldrb   %w[k0], [%[in]]\n\t"
720
        "orr    %[k1], %[k1], %[k0]\n\t"
721
722
        "L_siphash_last_done:\n\t"
723
724
        "eor    %[v3], %[v3], %[k1]\n\t"
725
#if WOLFSSL_SIPHASH_CROUNDS == 1
726
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
727
#elif WOLFSSL_SIPHASH_CROUNDS == 2
728
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
729
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
730
#endif
731
        "eor    %[v0], %[v0], %[k1]\n\t"
732
733
        "cmp    %w[outSz], #8\n\t"
734
        "b.eq   L_siphash_8_end\n\t"
735
736
        : [in] "+r" (in), [inSz] "+r" (inSz), [k0] "+r" (k0), [k1] "+r" (k1),
737
          [v0] "+r" (v0), [v1] "+r" (v1), [v2] "+r" (v2), [v3] "+r" (v3)
738
        : [key] "r" (key), [out] "r" (out) , [outSz] "r" (outSz)
739
        : "memory"
740
    );
741
742
    __asm__ __volatile__ (
743
744
        "mov    %w[k1], #0xee\n\t"
745
        "eor    %[v2], %[v2], %[k1]\n\t"
746
#if WOLFSSL_SIPHASH_DROUNDS == 2
747
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
748
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
749
#elif WOLFSSL_SIPHASH_DROUNDS == 4
750
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
751
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
752
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
753
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
754
#endif
755
        "eor    %[k0], %[v0], %[v1]\n\t"
756
        "eor    %[k1], %[v2], %[v3]\n\t"
757
        "eor    %[k0], %[k0], %[k1]\n\t"
758
759
        "mov    %w[k1], #0xdd\n\t"
760
        "eor    %[v1], %[v1], %[k1]\n\t"
761
#if WOLFSSL_SIPHASH_DROUNDS == 2
762
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
763
        SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
764
#elif WOLFSSL_SIPHASH_DROUNDS == 4
765
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
766
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
767
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
768
        SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
769
#endif
770
        "eor    %[k1], %[v3], %[v1]\n\t"
771
        "eor    %[k1], %[k1], %[v2]\n\t"
772
        "stp    %[k0], %[k1], [%[out]]\n\t"
773
        "b      L_siphash_done\n\t"
774
775
        "L_siphash_8_end:\n\t"
776
        "mov    %w[k1], #0xff\n\t"
777
        "eor    %[v2], %[v2], %[k1]\n\t"
778
#if WOLFSSL_SIPHASH_DROUNDS == 2
779
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
780
        SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
781
#elif WOLFSSL_SIPHASH_DROUNDS == 4
782
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
783
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
784
        SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
785
        SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
786
#endif
787
        "eor    %[k1], %[v3], %[v1]\n\t"
788
        "eor    %[k1], %[k1], %[v2]\n\t"
789
        "str    %[k1], [%[out]]\n\t"
790
791
        "L_siphash_done:\n\t"
792
793
        : [in] "+r" (in), [inSz] "+r" (inSz), [k0] "+r" (k0), [k1] "+r" (k1),
794
          [v0] "+r" (v0), [v1] "+r" (v1), [v2] "+r" (v2), [v3] "+r" (v3)
795
        : [key] "r" (key), [out] "r" (out) , [outSz] "r" (outSz)
796
        : "memory"
797
    );
798
799
    return 0;
800
}
801
802
#else
803
804
#define SipRoundV(v0, v1, v2, v3)   \
805
    (v0) += (v1);                   \
806
    (v2) += (v3);                   \
807
    (v1) = rotlFixed64(v1, 13);     \
808
    (v3) = rotlFixed64(v3, 16);     \
809
    (v1) ^= (v0);                   \
810
    (v3) ^= (v2);                   \
811
    (v0) = rotlFixed64(v0, 32);     \
812
    (v2) += (v1);                   \
813
    (v0) += (v3);                   \
814
    (v1) = rotlFixed64(v1, 17);     \
815
    (v3) = rotlFixed64(v3, 21);     \
816
    (v1) ^= (v2);                   \
817
    (v3) ^= (v0);                   \
818
    (v2) = rotlFixed64(v2, 32);
819
820
#define SipHashCompressV(v0, v1, v2, v3, m)             \
821
    do {                                                \
822
        int i;                                          \
823
        (v3) ^= (m);                                    \
824
        for (i = 0; i < WOLFSSL_SIPHASH_CROUNDS; i++) { \
825
            SipRoundV(v0, v1, v2, v3);                  \
826
        }                                               \
827
        (v0) ^= (m);                                    \
828
    }                                                   \
829
    while (0)
830
831
#define SipHashOutV(v0, v1, v2, v3, out)                \
832
    do {                                                \
833
        word64 n;                                       \
834
        int i;                                          \
835
                                                        \
836
        for (i = 0; i < WOLFSSL_SIPHASH_DROUNDS; i++) { \
837
            SipRoundV(v0, v1, v2, v3);                  \
838
        }                                               \
839
        n = (v0) ^ (v1) ^ (v2) ^ (v3);                  \
840
        SET_U64(out, n);                                \
841
    }                                                   \
842
    while (0)
843
844
/**
845
 * Perform SipHash operation on input with key.
846
 *
847
 * @param [in]      key      16 byte array - little endian.
848
 * @param [in]      in       Input message.
849
 * @param [in]      inSz     Size of input message.
850
 * @param [out]     out      Buffer to place MAC into.
851
 * @param [in]      outSz    Size of output MAC. 8 or 16 only.
852
 * @return  BAD_FUNC_ARG when key or out is NULL.
853
 * @return  BAD_FUNC_ARG when in is NULL and inSz is not zero.
854
 * @return  BAD_FUNC_ARG when outSz is not 8 nor 16.
855
 * @return  0 on success.
856
 */
857
int wc_SipHash(const unsigned char* key, const unsigned char* in, word32 inSz,
858
    unsigned char* out, unsigned char outSz)
859
{
860
    int ret = 0;
861
862
    if ((key == NULL) || ((in == NULL) && (inSz != 0)) || (out == NULL) ||
863
            ((outSz != SIPHASH_MAC_SIZE_8) && (outSz != SIPHASH_MAC_SIZE_16))) {
864
        ret = BAD_FUNC_ARG;
865
    }
866
867
    if (ret == 0) {
868
        word64 v0, v1, v2, v3;
869
        word64 k0 = GET_U64(key + 0);
870
        word64 k1 = GET_U64(key + 8);
871
        word64 b = (word64)((word64)inSz << 56);
872
873
        /* Initialize state with key. */
874
        v0 = 0x736f6d6570736575ULL;
875
        v1 = 0x646f72616e646f6dULL;
876
        v2 = 0x6c7967656e657261ULL;
877
        v3 = 0x7465646279746573ULL;
878
879
        if (outSz == SIPHASH_MAC_SIZE_16) {
880
            v1 ^= 0xee;
881
        }
882
883
        v0 ^= k0;
884
        v1 ^= k1;
885
        v2 ^= k0;
886
        v3 ^= k1;
887
888
        /* Process blocks from message. */
889
        while (inSz >= SIPHASH_BLOCK_SIZE) {
890
            word64 m = GET_U64(in);
891
            /* Compress the next block from the message data. */
892
            SipHashCompressV(v0, v1, v2, v3, m);
893
            in += SIPHASH_BLOCK_SIZE;
894
            inSz -= SIPHASH_BLOCK_SIZE;
895
        }
896
897
        switch (inSz) {
898
            case 7:
899
                b |= (word64)in[6] << 48;
900
                FALL_THROUGH;
901
            case 6:
902
                b |= (word64)in[5] << 40;
903
                FALL_THROUGH;
904
            case 5:
905
                b |= (word64)in[4] << 32;
906
                FALL_THROUGH;
907
            case 4:
908
                b |= (word64)GET_U32(in);
909
                break;
910
            case 3:
911
                b |= (word64)in[2] << 16;
912
                FALL_THROUGH;
913
            case 2:
914
                b |= (word64)GET_U16(in);
915
                break;
916
            case 1:
917
                b |= (word64)in[0];
918
                break;
919
            case 0:
920
                break;
921
        }
922
        SipHashCompressV(v0, v1, v2, v3, b);
923
924
        /* Output either 8 or 16 bytes. */
925
        if (outSz == SIPHASH_MAC_SIZE_8) {
926
            v2 ^= (word64)0xff;
927
            SipHashOutV(v0, v1, v2, v3, out);
928
        }
929
        else {
930
            v2 ^= (word64)0xee;
931
            SipHashOutV(v0, v1, v2, v3, out);
932
            v1 ^= (word64)0xdd;
933
            SipHashOutV(v0, v1, v2, v3, out + 8);
934
        }
935
    }
936
937
    return ret;
938
}
939
#endif /* !ASM */
940
941
#endif /* WOLFSSL_SIPHASH */