Coverage Report

Created: 2024-11-21 07:03

/src/openssl/crypto/siphash/siphash.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2017-2022 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
/* Based on https://131002.net/siphash C reference implementation */
11
/*
12
   SipHash reference C implementation
13
14
   Copyright (c) 2012-2016 Jean-Philippe Aumasson
15
   Copyright (c) 2012-2014 Daniel J. Bernstein
16
17
   To the extent possible under law, the author(s) have dedicated all copyright
18
   and related and neighboring rights to this software to the public domain
19
   worldwide. This software is distributed without any warranty.
20
21
   You should have received a copy of the CC0 Public Domain Dedication along
22
   with this software. If not, see
23
   <http://creativecommons.org/publicdomain/zero/1.0/>.
24
 */
25
26
#include <stdlib.h>
27
#include <string.h>
28
#include <openssl/crypto.h>
29
30
#include "crypto/siphash.h"
31
32
58.1k
#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
33
34
#define U32TO8_LE(p, v)                                                        \
35
16
    (p)[0] = (uint8_t)((v));                                                   \
36
16
    (p)[1] = (uint8_t)((v) >> 8);                                              \
37
16
    (p)[2] = (uint8_t)((v) >> 16);                                             \
38
16
    (p)[3] = (uint8_t)((v) >> 24);
39
40
#define U64TO8_LE(p, v)                                                        \
41
8
    U32TO8_LE((p), (uint32_t)((v)));                                           \
42
8
    U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
43
44
#define U8TO64_LE(p)                                                           \
45
6.78k
    (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) |                        \
46
6.78k
     ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) |                 \
47
6.78k
     ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) |                 \
48
6.78k
     ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
49
50
#define SIPROUND                                                               \
51
9.77k
    do {                                                                       \
52
9.68k
        v0 += v1;                                                              \
53
9.68k
        v1 = ROTL(v1, 13);                                                     \
54
9.68k
        v1 ^= v0;                                                              \
55
9.68k
        v0 = ROTL(v0, 32);                                                     \
56
9.68k
        v2 += v3;                                                              \
57
9.68k
        v3 = ROTL(v3, 16);                                                     \
58
9.68k
        v3 ^= v2;                                                              \
59
9.68k
        v0 += v3;                                                              \
60
9.68k
        v3 = ROTL(v3, 21);                                                     \
61
9.68k
        v3 ^= v0;                                                              \
62
9.68k
        v2 += v1;                                                              \
63
9.68k
        v1 = ROTL(v1, 17);                                                     \
64
9.68k
        v1 ^= v2;                                                              \
65
9.68k
        v2 = ROTL(v2, 32);                                                     \
66
9.68k
    } while (0)
67
68
size_t SipHash_ctx_size(void)
69
0
{
70
0
    return sizeof(SIPHASH);
71
0
}
72
73
size_t SipHash_hash_size(SIPHASH *ctx)
74
40
{
75
40
    return ctx->hash_size;
76
40
}
77
78
static size_t siphash_adjust_hash_size(size_t hash_size)
79
101
{
80
101
    if (hash_size == 0)
81
48
        hash_size = SIPHASH_MAX_DIGEST_SIZE;
82
101
    return hash_size;
83
101
}
84
85
int SipHash_set_hash_size(SIPHASH *ctx, size_t hash_size)
86
48
{
87
48
    hash_size = siphash_adjust_hash_size(hash_size);
88
48
    if (hash_size != SIPHASH_MIN_DIGEST_SIZE
89
48
        && hash_size != SIPHASH_MAX_DIGEST_SIZE)
90
0
        return 0;
91
92
    /*
93
     * It's possible that the key was set first.  If the hash size changes,
94
     * we need to adjust v1 (see SipHash_Init().
95
     */
96
97
    /* Start by adjusting the stored size, to make things easier */
98
48
    ctx->hash_size = siphash_adjust_hash_size(ctx->hash_size);
99
100
    /* Now, adjust ctx->v1 if the old and the new size differ */
101
48
    if ((size_t)ctx->hash_size != hash_size) {
102
30
        ctx->v1 ^= 0xee;
103
30
        ctx->hash_size = hash_size;
104
30
    }
105
48
    return 1;
106
48
}
107
108
/* hash_size = crounds = drounds = 0 means SipHash24 with 16-byte output */
109
int SipHash_Init(SIPHASH *ctx, const unsigned char *k, int crounds, int drounds)
110
5
{
111
5
    uint64_t k0 = U8TO64_LE(k);
112
5
    uint64_t k1 = U8TO64_LE(k + 8);
113
114
    /* If the hash size wasn't set, i.e. is zero */
115
5
    ctx->hash_size = siphash_adjust_hash_size(ctx->hash_size);
116
117
5
    if (drounds == 0)
118
0
        drounds = SIPHASH_D_ROUNDS;
119
5
    if (crounds == 0)
120
0
        crounds = SIPHASH_C_ROUNDS;
121
122
5
    ctx->crounds = crounds;
123
5
    ctx->drounds = drounds;
124
125
5
    ctx->len = 0;
126
5
    ctx->total_inlen = 0;
127
128
5
    ctx->v0 = 0x736f6d6570736575ULL ^ k0;
129
5
    ctx->v1 = 0x646f72616e646f6dULL ^ k1;
130
5
    ctx->v2 = 0x6c7967656e657261ULL ^ k0;
131
5
    ctx->v3 = 0x7465646279746573ULL ^ k1;
132
133
5
    if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
134
3
        ctx->v1 ^= 0xee;
135
136
5
    return 1;
137
5
}
138
139
void SipHash_Update(SIPHASH *ctx, const unsigned char *in, size_t inlen)
140
146
{
141
146
    uint64_t m;
142
146
    const uint8_t *end;
143
146
    int left;
144
146
    unsigned int i;
145
146
    uint64_t v0 = ctx->v0;
146
146
    uint64_t v1 = ctx->v1;
147
146
    uint64_t v2 = ctx->v2;
148
146
    uint64_t v3 = ctx->v3;
149
150
146
    ctx->total_inlen += inlen;
151
152
146
    if (ctx->len) {
153
        /* deal with leavings */
154
108
        size_t available = SIPHASH_BLOCK_SIZE - ctx->len;
155
156
        /* not enough to fill leavings */
157
108
        if (inlen < available) {
158
37
            memcpy(&ctx->leavings[ctx->len], in, inlen);
159
37
            ctx->len += inlen;
160
37
            return;
161
37
        }
162
163
        /* copy data into leavings and reduce input */
164
71
        memcpy(&ctx->leavings[ctx->len], in, available);
165
71
        inlen -= available;
166
71
        in += available;
167
168
        /* process leavings */
169
71
        m = U8TO64_LE(ctx->leavings);
170
71
        v3 ^= m;
171
113
        for (i = 0; i < ctx->crounds; ++i)
172
42
            SIPROUND;
173
71
        v0 ^= m;
174
71
    }
175
109
    left = inlen & (SIPHASH_BLOCK_SIZE-1); /* gets put into leavings */
176
109
    end = in + inlen - left;
177
178
6.80k
    for (; in != end; in += 8) {
179
6.69k
        m = U8TO64_LE(in);
180
6.69k
        v3 ^= m;
181
16.3k
        for (i = 0; i < ctx->crounds; ++i)
182
9.60k
            SIPROUND;
183
6.69k
        v0 ^= m;
184
6.69k
    }
185
186
    /* save leavings and other ctx */
187
109
    if (left)
188
89
        memcpy(ctx->leavings, end, left);
189
109
    ctx->len = left;
190
191
109
    ctx->v0 = v0;
192
109
    ctx->v1 = v1;
193
109
    ctx->v2 = v2;
194
109
    ctx->v3 = v3;
195
109
}
196
197
int SipHash_Final(SIPHASH *ctx, unsigned char *out, size_t outlen)
198
20
{
199
    /* finalize hash */
200
20
    unsigned int i;
201
20
    uint64_t b = ctx->total_inlen << 56;
202
20
    uint64_t v0 = ctx->v0;
203
20
    uint64_t v1 = ctx->v1;
204
20
    uint64_t v2 = ctx->v2;
205
20
    uint64_t v3 = ctx->v3;
206
207
20
    if (ctx->crounds == 0 || outlen == 0 || outlen != (size_t)ctx->hash_size)
208
15
        return 0;
209
210
5
    switch (ctx->len) {
211
1
    case 7:
212
1
        b |= ((uint64_t)ctx->leavings[6]) << 48;
213
        /* fall through */
214
1
    case 6:
215
1
        b |= ((uint64_t)ctx->leavings[5]) << 40;
216
        /* fall through */
217
4
    case 5:
218
4
        b |= ((uint64_t)ctx->leavings[4]) << 32;
219
        /* fall through */
220
4
    case 4:
221
4
        b |= ((uint64_t)ctx->leavings[3]) << 24;
222
        /* fall through */
223
5
    case 3:
224
5
        b |= ((uint64_t)ctx->leavings[2]) << 16;
225
        /* fall through */
226
5
    case 2:
227
5
        b |= ((uint64_t)ctx->leavings[1]) <<  8;
228
        /* fall through */
229
5
    case 1:
230
5
        b |= ((uint64_t)ctx->leavings[0]);
231
5
    case 0:
232
5
        break;
233
5
    }
234
235
5
    v3 ^= b;
236
15
    for (i = 0; i < ctx->crounds; ++i)
237
10
        SIPROUND;
238
5
    v0 ^= b;
239
5
    if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
240
3
        v2 ^= 0xee;
241
2
    else
242
2
        v2 ^= 0xff;
243
25
    for (i = 0; i < ctx->drounds; ++i)
244
20
        SIPROUND;
245
5
    b = v0 ^ v1 ^ v2  ^ v3;
246
5
    U64TO8_LE(out, b);
247
5
    if (ctx->hash_size == SIPHASH_MIN_DIGEST_SIZE)
248
2
        return 1;
249
3
    v1 ^= 0xdd;
250
15
    for (i = 0; i < ctx->drounds; ++i)
251
12
        SIPROUND;
252
3
    b = v0 ^ v1 ^ v2  ^ v3;
253
3
    U64TO8_LE(out + 8, b);
254
3
    return 1;
255
5
}