Coverage Report

Created: 2025-04-11 06:45

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