Files
wolfssl/wolfcrypt/src/siphash.c

938 lines
30 KiB
C

/* siphash.c
*
* Copyright (C) 2006-2025 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
#include <wolfssl/wolfcrypt/siphash.h>
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
#else
#define WOLFSSL_MISC_INCLUDED
#include <wolfcrypt/src/misc.c>
#endif
/* DESCRIPTION
*
* SipHash is a PseudoRandom Function (PRF) that can be used with small
* messages (less than 256 bytes).
* SipHash can be used for Message Authentication Codes (MACs) and as such must
* be passed a secret key.
* https://eprint.iacr.org/2012/351.pdf
*
* SipHash is commonly used in hash tables.
* Do not use this as a hash not as a general purpose MAC.
*
* WOLFSSL_SIPHASH_CROUNDS and WOLFSSL_SIPHASH_DROUNDS can be defined at build
* time to change the algorithm.
* Default is SipHash-2-4:
* WOLFSSL_SIPHASH_CROUNDS = 2
* WOLFSSL_SIPHASH_DROUNDS = 4
*
* Inline assembly implementations of wc_SipHash() written for:
* - GCC for Intel x86_64
* - GCC for Aarch64.
*/
#ifdef WOLFSSL_SIPHASH
#ifdef LITTLE_ENDIAN_ORDER
/**
* Decode little-endian byte array to 64-bit number.
*
* @param [in] a Little-endian byte array.
* @return 64-bit number.
*/
#define GET_U64(a) readUnalignedWord64(a)
/**
* Decode little-endian byte array to 32-bit number.
*
* @param [in] a Little-endian byte array.
* @return 32-bit number.
*/
#define GET_U32(a) readUnalignedWord32(a)
/**
* Decode little-endian byte array to 16-bit number.
*
* @param [in] a Little-endian byte array.
* @return 16-bit number.
*/
#define GET_U16(a) (*(word16*)(a))
/**
* Encode 64-bit number to a little-endian byte array.
*
* @param [out] a Byte array to write into.
* @param [in] n Number to encode.
*/
#define SET_U64(a, n) writeUnalignedWord64(a, n)
#else
/**
* Decode little-endian byte array to 64-bit number.
*
* @param [in] a Little-endian byte array.
* @return 64-bit number.
*/
#define GET_U64(a) (((word64)((a)[7]) << 56) | \
((word64)((a)[6]) << 48) | \
((word64)((a)[5]) << 40) | \
((word64)((a)[4]) << 32) | \
((word64)((a)[3]) << 24) | \
((word64)((a)[2]) << 16) | \
((word64)((a)[1]) << 8) | \
((word64)((a)[0]) ))
/**
* Decode little-endian byte array to 32-bit number.
*
* @param [in] a Little-endian byte array.
* @return 32-bit number.
*/
#define GET_U32(a) (((word32)((a)[3]) << 24) | \
((word32)((a)[2]) << 16) | \
((word32)((a)[1]) << 8) | \
((word32)((a)[0]) ))
/**
* Decode little-endian byte array to 16-bit number.
*
* @param [in] a Little-endian byte array.
* @return 16-bit number.
*/
#define GET_U16(a) (((word16)((a)[1]) << 8) | \
((word16)((a)[0]) ))
/**
* Encode 64-bit number to a little-endian byte array.
*
* @param [out] a Byte array to write into.
* @param [in] n Number to encode.
*/
#define SET_U64(a, n) (a)[0] = (byte)((n) ); \
(a)[1] = (byte)((n) >> 8); \
(a)[2] = (byte)((n) >> 16); \
(a)[3] = (byte)((n) >> 24); \
(a)[4] = (byte)((n) >> 32); \
(a)[5] = (byte)((n) >> 40); \
(a)[6] = (byte)((n) >> 48); \
(a)[7] = (byte)((n) >> 56)
#endif
/**
* Initialize SipHash operation with a key.
*
* @param [out] sipHash SipHash object.
* @param [in] key 16 byte array - little endian.
* @return BAD_FUNC_ARG when sipHash or key is NULL.
* @return BAD_FUNC_ARG when outSz is neither 8 nor 16.
* @return 0 on success.
*/
int wc_InitSipHash(SipHash* sipHash, const unsigned char* key,
unsigned char outSz)
{
int ret = 0;
/* Validate parameters. */
if ((sipHash == NULL) || (key == NULL) ||
((outSz != SIPHASH_MAC_SIZE_8) && (outSz != SIPHASH_MAC_SIZE_16))) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
word64 k0 = GET_U64(key + 0);
word64 k1 = GET_U64(key + 8);
/* Initialize state with key. */
sipHash->v[0] = W64LIT(0x736f6d6570736575);
if (outSz == SIPHASH_MAC_SIZE_8) {
sipHash->v[1] = W64LIT(0x646f72616e646f6d);
}
else {
sipHash->v[1] = W64LIT(0x646f72616e646f83);
}
sipHash->v[2] = W64LIT(0x6c7967656e657261);
sipHash->v[3] = W64LIT(0x7465646279746573);
sipHash->v[0] ^= k0;
sipHash->v[1] ^= k1;
sipHash->v[2] ^= k0;
sipHash->v[3] ^= k1;
/* No cached message bytes. */
sipHash->cacheCnt = 0;
/* No message bytes compressed yet. */
sipHash->inCnt = 0;
/* Keep the output size to check against final call. */
sipHash->outSz = outSz;
}
return ret;
}
/**
* One round of SipHash.
*
* @param [in, out] sipHash SipHash object.
*/
static WC_INLINE void SipRound(SipHash *sipHash)
{
word64* v = sipHash->v;
v[0] += v[1];
v[2] += v[3];
v[1] = rotlFixed64(v[1], 13);
v[3] = rotlFixed64(v[3], 16);
v[1] ^= v[0];
v[3] ^= v[2];
v[0] = rotlFixed64(v[0], 32);
v[2] += v[1];
v[0] += v[3];
v[1] = rotlFixed64(v[1], 17);
v[3] = rotlFixed64(v[3], 21);
v[1] ^= v[2];
v[3] ^= v[0];
v[2] = rotlFixed64(v[2], 32);
}
/**
* One step of the compression operation.
*
* @param [in, out] sipHash SipHash object.
* @param [in] m Message to compress.
*/
static WC_INLINE void SipHashCompress(SipHash* sipHash, const byte* m)
{
int i;
sipHash->v[3] ^= GET_U64(m);
for (i = 0; i < WOLFSSL_SIPHASH_CROUNDS; i++) {
SipRound(sipHash);
}
sipHash->v[0] ^= GET_U64(m);
}
/**
* Update the SipHash operation with more data.
*
* @param [in, out] sipHash SipHash object.
* @param [in] in Input message.
* @param [in] inSz Size of input message.
* @return BAD_FUNC_ARG when sipHash is NULL.
* @return BAD_FUNC_ARG when in is NULL and inSz is not zero.
* @return 0 on success.
*/
int wc_SipHashUpdate(SipHash* sipHash, const unsigned char* in, word32 inSz)
{
int ret = 0;
/* Validate parameters. */
if ((sipHash == NULL) || ((in == NULL) && (inSz != 0))) {
ret = BAD_FUNC_ARG;
}
/* Process any message bytes. */
if ((ret == 0) && (inSz > 0)) {
/* Add to cache if already started. */
if (sipHash->cacheCnt > 0) {
byte len = (byte)(SIPHASH_BLOCK_SIZE - sipHash->cacheCnt);
if (len > inSz) {
len = (byte)inSz;
}
XMEMCPY(sipHash->cache + sipHash->cacheCnt, in, len);
in += len;
inSz -= len;
sipHash->cacheCnt = (byte)(sipHash->cacheCnt + len);
if (sipHash->cacheCnt == SIPHASH_BLOCK_SIZE) {
/* Compress the block from the cache. */
SipHashCompress(sipHash, sipHash->cache);
sipHash->inCnt += SIPHASH_BLOCK_SIZE;
sipHash->cacheCnt = 0;
}
}
/* Process more blocks from message. */
while (inSz >= SIPHASH_BLOCK_SIZE) {
/* Compress the next block from the message data. */
SipHashCompress(sipHash, in);
in += SIPHASH_BLOCK_SIZE;
inSz -= SIPHASH_BLOCK_SIZE;
sipHash->inCnt += SIPHASH_BLOCK_SIZE;
}
if (inSz > 0) {
/* Cache remaining message bytes less than a block. */
XMEMCPY(sipHash->cache, in, inSz);
sipHash->cacheCnt = (byte)inSz;
}
}
return ret;
}
/**
* Calculate 8-bytes of output.
*
* @param [in, out] sipHash SipHash object.
* @param [out] out Buffer to place 8-bytes of MAC into.
*/
static WC_INLINE void SipHashOut(SipHash* sipHash, byte* out)
{
word64 n;
int i;
for (i = 0; i < WOLFSSL_SIPHASH_DROUNDS; i++) {
SipRound(sipHash);
}
n = sipHash->v[0] ^ sipHash->v[1] ^ sipHash->v[2] ^ sipHash->v[3];
SET_U64(out, n);
}
/**
* Finalize SipHash operation.
*
* @param [in, out] sipHash SipHash object.
* @param [out] out Buffer to place MAC into.
* @param [in] outSz Size of output MAC. 8 or 16 only.
* @return BAD_FUNC_ARG when sipHash or out is NULL.
* @return BAD_FUNC_ARG when outSz is not the same as initialized value.
* @return 0 on success.
*/
int wc_SipHashFinal(SipHash* sipHash, unsigned char* out, unsigned char outSz)
{
int ret = 0;
/* Validate parameters. */
if ((sipHash == NULL) || (out == NULL) || (outSz != sipHash->outSz)) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
/* Put in remaining cached message bytes. */
XMEMSET(sipHash->cache + sipHash->cacheCnt, 0, 7U - sipHash->cacheCnt);
sipHash->cache[7] = (byte)(sipHash->inCnt + sipHash->cacheCnt);
SipHashCompress(sipHash, sipHash->cache);
sipHash->cacheCnt = 0;
/* Output either 8 or 16 bytes. */
if (outSz == SIPHASH_MAC_SIZE_8) {
sipHash->v[2] ^= (word64)0xff;
SipHashOut(sipHash, out);
}
else {
sipHash->v[2] ^= (word64)0xee;
SipHashOut(sipHash, out);
sipHash->v[1] ^= (word64)0xdd;
SipHashOut(sipHash, out + 8);
}
}
return ret;
}
#if !defined(WOLFSSL_NO_ASM) && defined(__GNUC__) && defined(__x86_64__) && \
(WOLFSSL_SIPHASH_CROUNDS == 1 || WOLFSSL_SIPHASH_CROUNDS == 2) && \
(WOLFSSL_SIPHASH_DROUNDS == 2 || WOLFSSL_SIPHASH_DROUNDS == 4)
#define SIPHASH_ROUND(v0, v1, v2, v3) \
"addq " #v1 ", " #v0 "\n\t" \
"addq " #v3 ", " #v2 "\n\t" \
"rolq $13, " #v1 "\n\t" \
"rolq $16, " #v3 "\n\t" \
"xorq " #v0 ", " #v1 "\n\t" \
"xorq " #v2 ", " #v3 "\n\t" \
"rolq $32, " #v0 "\n\t" \
"addq " #v1 ", " #v2 "\n\t" \
"addq " #v3 ", " #v0 "\n\t" \
"rolq $17, " #v1 "\n\t" \
"rolq $21, " #v3 "\n\t" \
"xorq " #v2 ", " #v1 "\n\t" \
"xorq " #v0 ", " #v3 "\n\t" \
"rolq $32, " #v2 "\n\t"
#define SIPHASH_LAST_ROUND(v0, v1, v2, v3) \
"addq " #v1 ", " #v0 "\n\t" \
"addq " #v3 ", " #v2 "\n\t" \
"rolq $13, " #v1 "\n\t" \
"rolq $16, " #v3 "\n\t" \
"xorq " #v0 ", " #v1 "\n\t" \
"xorq " #v2 ", " #v3 "\n\t" \
"addq " #v1 ", " #v2 "\n\t" \
"rolq $17, " #v1 "\n\t" \
"rolq $21, " #v3 "\n\t" \
"xorq " #v2 ", " #v1 "\n\t" \
"rolq $32, " #v2 "\n\t"
/**
* Perform SipHash operation on input with key.
*
* @param [in] key 16 byte array - little endian.
* @param [in] in Input message.
* @param [in] inSz Size of input message.
* @param [out] out Buffer to place MAC into.
* @param [in] outSz Size of output MAC. 8 or 16 only.
* @return BAD_FUNC_ARG when key or out is NULL.
* @return BAD_FUNC_ARG when in is NULL and inSz is not zero.
* @return BAD_FUNC_ARG when outSz is neither 8 nor 16.
* @return 0 on success.
*/
int wc_SipHash(const unsigned char* key, const unsigned char* in, word32 inSz,
unsigned char* out, unsigned char outSz)
{
word64 v0 = 0x736f6d6570736575L;
word64 v1 = 0x646f72616e646f6dL;
word64 v2 = 0x6c7967656e657261L;
word64 v3 = 0x7465646279746573L;
word64 k0;
word64 k1;
if ((key == NULL) || ((in == NULL) && (inSz != 0)) || (out == NULL) ||
((outSz != SIPHASH_MAC_SIZE_8) && (outSz != SIPHASH_MAC_SIZE_16))) {
return BAD_FUNC_ARG;
}
k0 = ((word64*)key)[0];
k1 = ((word64*)key)[1];
__asm__ __volatile__ (
"xorq %[k0], %[v0]\n\t"
"xorq %[k1], %[v1]\n\t"
"xorq %[k0], %[v2]\n\t"
"xorq %[k1], %[v3]\n\t"
"cmp $8, %[outSz]\n\t"
"mov %[inSz], %k[k1]\n\t"
"je L_siphash_8_top\n\t"
"xorq $0xee, %[v1]\n\t"
"L_siphash_8_top:\n\t"
"sub $8, %[inSz]\n\t"
"jb L_siphash_done_input_8\n\t"
"L_siphash_input:\n\t"
"movq (%[in]), %[k0]\n\t"
"addq $8, %[in]\n\t"
"xorq %[k0], %[v3]\n\t"
#if WOLFSSL_SIPHASH_CROUNDS == 1
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
#elif WOLFSSL_SIPHASH_CROUNDS == 2
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
#endif
"xorq %[k0], %[v0]\n\t"
"sub $8, %[inSz]\n\t"
"jge L_siphash_input\n\t"
"L_siphash_done_input_8:\n\t"
"add $8, %[inSz]\n\t"
"shlq $56, %[k1]\n\t"
"cmp $0, %[inSz]\n\t"
"je L_siphash_last_done\n\t"
"cmp $4, %[inSz]\n\t"
"jl L_siphash_last_lt4\n\t"
"cmp $7, %[inSz]\n\t"
"jl L_siphash_n7\n\t"
"movzbq 6(%[in]), %[k0]\n\t"
"shlq $48, %[k0]\n\t"
"orq %[k0], %[k1]\n\t"
"L_siphash_n7:\n\t"
"cmp $6, %[inSz]\n\t"
"jl L_siphash_n6\n\t"
"movzbq 5(%[in]), %[k0]\n\t"
"shlq $40, %[k0]\n\t"
"orq %[k0], %[k1]\n\t"
"L_siphash_n6:\n\t"
: [in] "+r" (in), [inSz] "+r" (inSz), [k0] "+r" (k0), [k1] "+r" (k1),
[v0] "+r" (v0), [v1] "+r" (v1), [v2] "+r" (v2), [v3] "+r" (v3)
: [out] "r" (out) , [outSz] "r" (outSz)
: "memory"
);
__asm__ __volatile__ (
"cmp $5, %[inSz]\n\t"
"jl L_siphash_n5\n\t"
"movzbq 4(%[in]), %[k0]\n\t"
"shlq $32, %[k0]\n\t"
"orq %[k0], %[k1]\n\t"
"L_siphash_n5:\n\t"
"mov (%[in]), %k[k0]\n\t"
"orq %[k0], %[k1]\n\t"
"jmp L_siphash_last_done\n\t"
"L_siphash_last_lt4:\n\t"
"cmp $1, %[inSz]\n\t"
"je L_siphash_last_1\n\t"
"cmp $3, %[inSz]\n\t"
"jl L_siphash_n3\n\t"
"movzbq 2(%[in]), %[k0]\n\t"
"shlq $16, %[k0]\n\t"
"orq %[k0], %[k1]\n\t"
"L_siphash_n3:\n\t"
"movw (%[in]), %w[k0]\n\t"
"or %w[k0], %w[k1]\n\t"
"jmp L_siphash_last_done\n\t"
"L_siphash_last_1:\n\t"
"movb (%[in]), %b[k0]\n\t"
"or %b[k0], %b[k1]\n\t"
"L_siphash_last_done:\n\t"
"xorq %[k1], %[v3]\n\t"
#if WOLFSSL_SIPHASH_CROUNDS == 1
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
#elif WOLFSSL_SIPHASH_CROUNDS == 2
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
#endif
"xorq %[k1], %[v0]\n\t"
: [in] "+r" (in), [inSz] "+r" (inSz), [k0] "+r" (k0), [k1] "+r" (k1),
[v0] "+r" (v0), [v1] "+r" (v1), [v2] "+r" (v2), [v3] "+r" (v3)
: [out] "r" (out) , [outSz] "r" (outSz)
: "memory"
);
__asm__ __volatile__ (
"cmp $8, %[outSz]\n\t"
"je L_siphash_8_end\n\t"
"xor $0xee, %b[v2]\n\t"
#if WOLFSSL_SIPHASH_DROUNDS == 2
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
#elif WOLFSSL_SIPHASH_DROUNDS == 4
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
#endif
"movq %[v0], %[k0]\n\t"
"xorq %[v1], %[k0]\n\t"
"xorq %[v2], %[k0]\n\t"
"xorq %[v3], %[k0]\n\t"
"movq %[k0], (%[out])\n\t"
"xor $0xdd, %b[v1]\n\t"
#if WOLFSSL_SIPHASH_DROUNDS == 2
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
#elif WOLFSSL_SIPHASH_DROUNDS == 4
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
#endif
"xorq %[v3], %[v1]\n\t"
"xorq %[v2], %[v1]\n\t"
"movq %[v1], 8(%[out])\n\t"
"jmp L_siphash_done\n\t"
"L_siphash_8_end:\n\t"
"xor $0xff, %b[v2]\n\t"
#if WOLFSSL_SIPHASH_DROUNDS == 2
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
#elif WOLFSSL_SIPHASH_DROUNDS == 4
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
#endif
"xorq %[v3], %[v1]\n\t"
"xorq %[v2], %[v1]\n\t"
"movq %[v1], (%[out])\n\t"
"L_siphash_done:\n\t"
: [in] "+r" (in), [inSz] "+r" (inSz), [k0] "+r" (k0), [k1] "+r" (k1),
[v0] "+r" (v0), [v1] "+r" (v1), [v2] "+r" (v2), [v3] "+r" (v3)
: [out] "r" (out) , [outSz] "r" (outSz)
: "memory"
);
return 0;
}
#elif defined(WOLFSSL_ARMASM) && defined(__GNUC__) && defined(__aarch64__) && \
(WOLFSSL_SIPHASH_CROUNDS == 1 || WOLFSSL_SIPHASH_CROUNDS == 2) && \
(WOLFSSL_SIPHASH_DROUNDS == 2 || WOLFSSL_SIPHASH_DROUNDS == 4)
#define SIPHASH_ROUND(v0, v1, v2, v3) \
"add " #v0 ", " #v0 ", " #v1 "\n\t" \
"add " #v2 ", " #v2 ", " #v3 "\n\t" \
"ror " #v1 ", " #v1 ", #51\n\t" \
"ror " #v3 ", " #v3 ", #48\n\t" \
"eor " #v1 ", " #v1 ", " #v0 "\n\t" \
"eor " #v3 ", " #v3 ", " #v2 "\n\t" \
"ror " #v0 ", " #v0 ", #32\n\t" \
"add " #v2 ", " #v2 ", " #v1 "\n\t" \
"add " #v0 ", " #v0 ", " #v3 "\n\t" \
"ror " #v1 ", " #v1 ", #47\n\t" \
"ror " #v3 ", " #v3 ", #43\n\t" \
"eor " #v1 ", " #v1 ", " #v2 "\n\t" \
"eor " #v3 ", " #v3 ", " #v0 "\n\t" \
"ror " #v2 ", " #v2 ", #32\n\t"
#define SIPHASH_LAST_ROUND(v0, v1, v2, v3) \
"add " #v0 ", " #v0 ", " #v1 "\n\t" \
"add " #v2 ", " #v2 ", " #v3 "\n\t" \
"ror " #v1 ", " #v1 ", #51\n\t" \
"ror " #v3 ", " #v3 ", #48\n\t" \
"eor " #v1 ", " #v1 ", " #v0 "\n\t" \
"eor " #v3 ", " #v3 ", " #v2 "\n\t" \
"add " #v2 ", " #v2 ", " #v1 "\n\t" \
"ror " #v1 ", " #v1 ", #47\n\t" \
"ror " #v3 ", " #v3 ", #43\n\t" \
"eor " #v1 ", " #v1 ", " #v2 "\n\t" \
"ror " #v2 ", " #v2 ", #32\n\t"
/**
* Perform SipHash operation on input with key.
*
* @param [in] key 16 byte array - little endian.
* @param [in] in Input message.
* @param [in] inSz Size of input message.
* @param [out] out Buffer to place MAC into.
* @param [in] outSz Size of output MAC. 8 or 16 only.
* @return BAD_FUNC_ARG when key or out is NULL.
* @return BAD_FUNC_ARG when in is NULL and inSz is not zero.
* @return BAD_FUNC_ARG when outSz is not 8 nor 16.
* @return 0 on success.
*/
int wc_SipHash(const unsigned char* key, const unsigned char* in, word32 inSz,
unsigned char* out, unsigned char outSz)
{
word64 v0 = 0x736f6d6570736575L;
word64 v1 = 0x646f72616e646f6dL;
word64 v2 = 0x6c7967656e657261L;
word64 v3 = 0x7465646279746573L;
word64 k0;
word64 k1;
if ((key == NULL) || ((in == NULL) && (inSz != 0)) || (out == NULL) ||
((outSz != SIPHASH_MAC_SIZE_8) && (outSz != SIPHASH_MAC_SIZE_16))) {
return BAD_FUNC_ARG;
}
k0 = ((word64*)key)[0];
k1 = ((word64*)key)[1];
__asm__ __volatile__ (
"eor %[v0], %[v0], %[k0]\n\t"
"eor %[v1], %[v1], %[k1]\n\t"
"eor %[v2], %[v2], %[k0]\n\t"
"eor %[v3], %[v3], %[k1]\n\t"
"mov %w[k1], %w[inSz]\n\t"
"cmp %w[outSz], #8\n\t"
"b.eq L_siphash_8_top\n\t"
"mov %w[k0], #0xee\n\t"
"eor %[v1], %[v1], %[k0]\n\t"
"L_siphash_8_top:\n\t"
"subs %w[inSz], %w[inSz], #8\n\t"
"b.mi L_siphash_done_input_8\n\t"
"L_siphash_input:\n\t"
"ldr %[k0], [%[in]], #8\n\t"
"eor %[v3], %[v3], %[k0]\n\t"
#if WOLFSSL_SIPHASH_CROUNDS == 1
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
#elif WOLFSSL_SIPHASH_CROUNDS == 2
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
#endif
"eor %[v0], %[v0], %[k0]\n\t"
"subs %w[inSz], %w[inSz], #8\n\t"
"b.ge L_siphash_input\n\t"
"L_siphash_done_input_8:\n\t"
"add %w[inSz], %w[inSz], #8\n\t"
"lsl %[k1], %[k1], #56\n\t"
"cmp %w[inSz], #0\n\t"
"b.eq L_siphash_last_done\n\t"
"cmp %w[inSz], #4\n\t"
"b.lt L_siphash_last_lt4\n\t"
"cmp %w[inSz], #7\n\t"
"b.lt L_siphash_n7\n\t"
"ldrb %w[k0], [%[in], 6]\n\t"
"orr %[k1], %[k1], %[k0], lsl 48\n\t"
"L_siphash_n7:\n\t"
"cmp %w[inSz], #6\n\t"
"b.lt L_siphash_n6\n\t"
"ldrb %w[k0], [%[in], 5]\n\t"
"orr %[k1], %[k1], %[k0], lsl 40\n\t"
"L_siphash_n6:\n\t"
"cmp %w[inSz], #5\n\t"
"b.lt L_siphash_n5\n\t"
"ldrb %w[k0], [%[in], 4]\n\t"
"orr %[k1], %[k1], %[k0], lsl 32\n\t"
"L_siphash_n5:\n\t"
"ldr %w[k0], [%[in]]\n\t"
"orr %[k1], %[k1], %[k0]\n\t"
"b L_siphash_last_done\n\t"
"L_siphash_last_lt4:\n\t"
"cmp %w[inSz], #1\n\t"
"b.eq L_siphash_last_1\n\t"
"cmp %w[inSz], #3\n\t"
"b.lt L_siphash_n3\n\t"
"ldrb %w[k0], [%[in], 2]\n\t"
"orr %[k1], %[k1], %[k0], lsl 16\n\t"
"L_siphash_n3:\n\t"
"ldrh %w[k0], [%[in]]\n\t"
"orr %[k1], %[k1], %[k0]\n\t"
"b L_siphash_last_done\n\t"
"L_siphash_last_1:\n\t"
"ldrb %w[k0], [%[in]]\n\t"
"orr %[k1], %[k1], %[k0]\n\t"
"L_siphash_last_done:\n\t"
"eor %[v3], %[v3], %[k1]\n\t"
#if WOLFSSL_SIPHASH_CROUNDS == 1
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
#elif WOLFSSL_SIPHASH_CROUNDS == 2
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
#endif
"eor %[v0], %[v0], %[k1]\n\t"
"cmp %w[outSz], #8\n\t"
"b.eq L_siphash_8_end\n\t"
: [in] "+r" (in), [inSz] "+r" (inSz), [k0] "+r" (k0), [k1] "+r" (k1),
[v0] "+r" (v0), [v1] "+r" (v1), [v2] "+r" (v2), [v3] "+r" (v3)
: [key] "r" (key), [out] "r" (out) , [outSz] "r" (outSz)
: "memory"
);
__asm__ __volatile__ (
"mov %w[k1], #0xee\n\t"
"eor %[v2], %[v2], %[k1]\n\t"
#if WOLFSSL_SIPHASH_DROUNDS == 2
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
#elif WOLFSSL_SIPHASH_DROUNDS == 4
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
#endif
"eor %[k0], %[v0], %[v1]\n\t"
"eor %[k1], %[v2], %[v3]\n\t"
"eor %[k0], %[k0], %[k1]\n\t"
"mov %w[k1], #0xdd\n\t"
"eor %[v1], %[v1], %[k1]\n\t"
#if WOLFSSL_SIPHASH_DROUNDS == 2
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
#elif WOLFSSL_SIPHASH_DROUNDS == 4
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
#endif
"eor %[k1], %[v3], %[v1]\n\t"
"eor %[k1], %[k1], %[v2]\n\t"
"stp %[k0], %[k1], [%[out]]\n\t"
"b L_siphash_done\n\t"
"L_siphash_8_end:\n\t"
"mov %w[k1], #0xff\n\t"
"eor %[v2], %[v2], %[k1]\n\t"
#if WOLFSSL_SIPHASH_DROUNDS == 2
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
#elif WOLFSSL_SIPHASH_DROUNDS == 4
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_ROUND(%[v0], %[v1], %[v2], %[v3])
SIPHASH_LAST_ROUND(%[v0], %[v1], %[v2], %[v3])
#endif
"eor %[k1], %[v3], %[v1]\n\t"
"eor %[k1], %[k1], %[v2]\n\t"
"str %[k1], [%[out]]\n\t"
"L_siphash_done:\n\t"
: [in] "+r" (in), [inSz] "+r" (inSz), [k0] "+r" (k0), [k1] "+r" (k1),
[v0] "+r" (v0), [v1] "+r" (v1), [v2] "+r" (v2), [v3] "+r" (v3)
: [key] "r" (key), [out] "r" (out) , [outSz] "r" (outSz)
: "memory"
);
return 0;
}
#else
#define SipRoundV(v0, v1, v2, v3) \
(v0) += (v1); \
(v2) += (v3); \
(v1) = rotlFixed64(v1, 13); \
(v3) = rotlFixed64(v3, 16); \
(v1) ^= (v0); \
(v3) ^= (v2); \
(v0) = rotlFixed64(v0, 32); \
(v2) += (v1); \
(v0) += (v3); \
(v1) = rotlFixed64(v1, 17); \
(v3) = rotlFixed64(v3, 21); \
(v1) ^= (v2); \
(v3) ^= (v0); \
(v2) = rotlFixed64(v2, 32);
#define SipHashCompressV(v0, v1, v2, v3, m) \
do { \
int i; \
(v3) ^= (m); \
for (i = 0; i < WOLFSSL_SIPHASH_CROUNDS; i++) { \
SipRoundV(v0, v1, v2, v3); \
} \
(v0) ^= (m); \
} \
while (0)
#define SipHashOutV(v0, v1, v2, v3, out) \
do { \
word64 n; \
int i; \
\
for (i = 0; i < WOLFSSL_SIPHASH_DROUNDS; i++) { \
SipRoundV(v0, v1, v2, v3); \
} \
n = (v0) ^ (v1) ^ (v2) ^ (v3); \
SET_U64(out, n); \
} \
while (0)
/**
* Perform SipHash operation on input with key.
*
* @param [in] key 16 byte array - little endian.
* @param [in] in Input message.
* @param [in] inSz Size of input message.
* @param [out] out Buffer to place MAC into.
* @param [in] outSz Size of output MAC. 8 or 16 only.
* @return BAD_FUNC_ARG when key or out is NULL.
* @return BAD_FUNC_ARG when in is NULL and inSz is not zero.
* @return BAD_FUNC_ARG when outSz is not 8 nor 16.
* @return 0 on success.
*/
int wc_SipHash(const unsigned char* key, const unsigned char* in, word32 inSz,
unsigned char* out, unsigned char outSz)
{
int ret = 0;
if ((key == NULL) || ((in == NULL) && (inSz != 0)) || (out == NULL) ||
((outSz != SIPHASH_MAC_SIZE_8) && (outSz != SIPHASH_MAC_SIZE_16))) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
word64 v0, v1, v2, v3;
word64 k0 = GET_U64(key + 0);
word64 k1 = GET_U64(key + 8);
word64 b = (word64)((word64)inSz << 56);
/* Initialize state with key. */
v0 = 0x736f6d6570736575ULL;
v1 = 0x646f72616e646f6dULL;
v2 = 0x6c7967656e657261ULL;
v3 = 0x7465646279746573ULL;
if (outSz == SIPHASH_MAC_SIZE_16) {
v1 ^= 0xee;
}
v0 ^= k0;
v1 ^= k1;
v2 ^= k0;
v3 ^= k1;
/* Process blocks from message. */
while (inSz >= SIPHASH_BLOCK_SIZE) {
word64 m = GET_U64(in);
/* Compress the next block from the message data. */
SipHashCompressV(v0, v1, v2, v3, m);
in += SIPHASH_BLOCK_SIZE;
inSz -= SIPHASH_BLOCK_SIZE;
}
switch (inSz) {
case 7:
b |= (word64)in[6] << 48;
FALL_THROUGH;
case 6:
b |= (word64)in[5] << 40;
FALL_THROUGH;
case 5:
b |= (word64)in[4] << 32;
FALL_THROUGH;
case 4:
b |= (word64)GET_U32(in);
break;
case 3:
b |= (word64)in[2] << 16;
FALL_THROUGH;
case 2:
b |= (word64)GET_U16(in);
break;
case 1:
b |= (word64)in[0];
break;
case 0:
break;
}
SipHashCompressV(v0, v1, v2, v3, b);
/* Output either 8 or 16 bytes. */
if (outSz == SIPHASH_MAC_SIZE_8) {
v2 ^= (word64)0xff;
SipHashOutV(v0, v1, v2, v3, out);
}
else {
v2 ^= (word64)0xee;
SipHashOutV(v0, v1, v2, v3, out);
v1 ^= (word64)0xdd;
SipHashOutV(v0, v1, v2, v3, out + 8);
}
}
return ret;
}
#endif /* !ASM */
#endif /* WOLFSSL_SIPHASH */