Coverage Report

Created: 2026-10-16 07:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl/crypto/modes/siv128.c
Line
Count
Source
1
/*
2
 * Copyright 2018-2026 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
#include <string.h>
11
#include <stdlib.h>
12
#include <openssl/crypto.h>
13
#include <openssl/evp.h>
14
#include <openssl/core_names.h>
15
#include <openssl/params.h>
16
#include "internal/endian.h"
17
#include "crypto/modes.h"
18
#include "crypto/siv.h"
19
20
#ifndef OPENSSL_NO_SIV
21
22
__owur static ossl_inline uint32_t rotl8(uint32_t x)
23
0
{
24
0
    return (x << 8) | (x >> 24);
25
0
}
26
27
__owur static ossl_inline uint32_t rotr8(uint32_t x)
28
0
{
29
0
    return (x >> 8) | (x << 24);
30
0
}
31
32
__owur static ossl_inline uint64_t byteswap8(uint64_t x)
33
0
{
34
0
    uint32_t high = (uint32_t)(x >> 32);
35
0
    uint32_t low = (uint32_t)x;
36
37
0
    high = (rotl8(high) & 0x00ff00ff) | (rotr8(high) & 0xff00ff00);
38
0
    low = (rotl8(low) & 0x00ff00ff) | (rotr8(low) & 0xff00ff00);
39
0
    return ((uint64_t)low) << 32 | (uint64_t)high;
40
0
}
41
42
__owur static ossl_inline uint64_t siv128_getword(SIV_BLOCK const *b, size_t i)
43
0
{
44
0
    DECLARE_IS_ENDIAN;
45
46
0
    if (IS_LITTLE_ENDIAN)
47
0
        return byteswap8(b->word[i]);
48
0
    return b->word[i];
49
0
}
50
51
static ossl_inline void siv128_putword(SIV_BLOCK *b, size_t i, uint64_t x)
52
0
{
53
0
    DECLARE_IS_ENDIAN;
54
55
0
    if (IS_LITTLE_ENDIAN)
56
0
        b->word[i] = byteswap8(x);
57
0
    else
58
0
        b->word[i] = x;
59
0
}
60
61
static ossl_inline void siv128_xorblock(SIV_BLOCK *x,
62
    SIV_BLOCK const *y)
63
0
{
64
0
    x->word[0] ^= y->word[0];
65
0
    x->word[1] ^= y->word[1];
66
0
}
67
68
/*
69
 * Doubles |b|, which is 16 bytes representing an element
70
 * of GF(2**128) modulo the irreducible polynomial
71
 * x**128 + x**7 + x**2 + x + 1.
72
 * Assumes two's-complement arithmetic
73
 */
74
static ossl_inline void siv128_dbl(SIV_BLOCK *b)
75
0
{
76
0
    uint64_t high = siv128_getword(b, 0);
77
0
    uint64_t low = siv128_getword(b, 1);
78
0
    uint64_t high_carry = high & (((uint64_t)1) << 63);
79
0
    uint64_t low_carry = low & (((uint64_t)1) << 63);
80
0
    int64_t low_mask = -((int64_t)(high_carry >> 63)) & 0x87;
81
0
    uint64_t high_mask = low_carry >> 63;
82
83
0
    high = (high << 1) | high_mask;
84
0
    low = (low << 1) ^ (uint64_t)low_mask;
85
0
    siv128_putword(b, 0, high);
86
0
    siv128_putword(b, 1, low);
87
0
}
88
89
__owur static ossl_inline int siv128_do_s2v_p(SIV128_CONTEXT *ctx, SIV_BLOCK *out,
90
    unsigned char const *in, size_t len)
91
0
{
92
0
    SIV_BLOCK t;
93
0
    size_t out_len = sizeof(out->byte);
94
0
    EVP_MAC_CTX *mac_ctx;
95
0
    int ret = 0;
96
97
0
    if (len > 0 && (in == NULL || out == NULL))
98
0
        return 0;
99
100
0
    mac_ctx = EVP_MAC_CTX_dup(ctx->mac_ctx_init);
101
0
    if (mac_ctx == NULL)
102
0
        return 0;
103
104
0
    if (len >= SIV_LEN) {
105
0
        if (!EVP_MAC_update(mac_ctx, in, len - SIV_LEN))
106
0
            goto err;
107
0
        memcpy(&t, in + (len - SIV_LEN), SIV_LEN);
108
0
        siv128_xorblock(&t, &ctx->d);
109
0
        if (!EVP_MAC_update(mac_ctx, t.byte, SIV_LEN))
110
0
            goto err;
111
0
    } else {
112
0
        memset(&t, 0, sizeof(t));
113
0
        if (len > 0)
114
0
            memcpy(&t, in, len);
115
0
        t.byte[len] = 0x80;
116
0
        siv128_dbl(&ctx->d);
117
0
        siv128_xorblock(&t, &ctx->d);
118
0
        if (!EVP_MAC_update(mac_ctx, t.byte, SIV_LEN))
119
0
            goto err;
120
0
    }
121
0
    if (!EVP_MAC_final(mac_ctx, out->byte, &out_len, sizeof(out->byte))
122
0
        || out_len != SIV_LEN)
123
0
        goto err;
124
125
0
    ret = 1;
126
127
0
err:
128
0
    EVP_MAC_CTX_free(mac_ctx);
129
0
    return ret;
130
0
}
131
132
__owur static ossl_inline int siv128_do_encrypt(EVP_CIPHER_CTX *ctx, unsigned char *out,
133
    unsigned char const *in, size_t len,
134
    SIV_BLOCK *icv)
135
0
{
136
0
    int out_len = (int)len;
137
138
0
    if (!EVP_CipherInit_ex(ctx, NULL, NULL, NULL, icv->byte, 1))
139
0
        return 0;
140
0
    return EVP_EncryptUpdate(ctx, out, &out_len, in, out_len);
141
0
}
142
143
/*
144
 * Create a new SIV128_CONTEXT
145
 */
146
SIV128_CONTEXT *ossl_siv128_new(const unsigned char *key, int klen,
147
    EVP_CIPHER *cbc, EVP_CIPHER *ctr,
148
    OSSL_LIB_CTX *libctx, const char *propq)
149
0
{
150
0
    SIV128_CONTEXT *ctx;
151
0
    int ret;
152
153
0
    if ((ctx = OPENSSL_malloc(sizeof(*ctx))) != NULL) {
154
0
        ret = ossl_siv128_init(ctx, key, klen, cbc, ctr, libctx, propq);
155
0
        if (ret)
156
0
            return ctx;
157
0
        OPENSSL_free(ctx);
158
0
    }
159
160
0
    return NULL;
161
0
}
162
163
/*
164
 * Initialise an existing SIV128_CONTEXT
165
 */
166
int ossl_siv128_init(SIV128_CONTEXT *ctx, const unsigned char *key, int klen,
167
    const EVP_CIPHER *cbc, const EVP_CIPHER *ctr,
168
    OSSL_LIB_CTX *libctx, const char *propq)
169
0
{
170
0
    static const unsigned char zero[SIV_LEN] = { 0 };
171
0
    size_t out_len = SIV_LEN;
172
0
    EVP_MAC_CTX *mac_ctx = NULL;
173
0
    OSSL_PARAM params[3];
174
0
    const char *cbc_name;
175
176
0
    if (ctx == NULL)
177
0
        return 0;
178
179
0
    memset(&ctx->d, 0, sizeof(ctx->d));
180
0
    EVP_CIPHER_CTX_free(ctx->cipher_ctx);
181
0
    EVP_MAC_CTX_free(ctx->mac_ctx_init);
182
0
    EVP_MAC_free(ctx->mac);
183
0
    ctx->mac = NULL;
184
0
    ctx->cipher_ctx = NULL;
185
0
    ctx->mac_ctx_init = NULL;
186
187
0
    if (key == NULL || cbc == NULL || ctr == NULL)
188
0
        return 0;
189
190
0
    cbc_name = EVP_CIPHER_get0_name(cbc);
191
0
    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER,
192
0
        (char *)cbc_name, 0);
193
0
    params[1] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY,
194
0
        (void *)key, klen);
195
0
    params[2] = OSSL_PARAM_construct_end();
196
197
0
    if ((ctx->cipher_ctx = EVP_CIPHER_CTX_new()) == NULL
198
0
        || (ctx->mac = EVP_MAC_fetch(libctx, OSSL_MAC_NAME_CMAC, propq)) == NULL
199
0
        || (ctx->mac_ctx_init = EVP_MAC_CTX_new(ctx->mac)) == NULL
200
0
        || !EVP_MAC_CTX_set_params(ctx->mac_ctx_init, params)
201
0
        || !EVP_EncryptInit_ex(ctx->cipher_ctx, ctr, NULL, key + klen, NULL)
202
0
        || (mac_ctx = EVP_MAC_CTX_dup(ctx->mac_ctx_init)) == NULL
203
0
        || !EVP_MAC_update(mac_ctx, zero, sizeof(zero))
204
0
        || !EVP_MAC_final(mac_ctx, ctx->d.byte, &out_len,
205
0
            sizeof(ctx->d.byte))) {
206
0
        EVP_CIPHER_CTX_free(ctx->cipher_ctx);
207
0
        ctx->cipher_ctx = NULL;
208
0
        EVP_MAC_CTX_free(ctx->mac_ctx_init);
209
0
        ctx->mac_ctx_init = NULL;
210
0
        EVP_MAC_CTX_free(mac_ctx);
211
0
        EVP_MAC_free(ctx->mac);
212
0
        ctx->mac = NULL;
213
0
        return 0;
214
0
    }
215
0
    EVP_MAC_CTX_free(mac_ctx);
216
217
0
    ctx->final_ret = -1;
218
0
    ctx->crypto_ok = 1;
219
220
0
    return 1;
221
0
}
222
223
/*
224
 * Copy an SIV128_CONTEXT object
225
 */
226
int ossl_siv128_copy_ctx(SIV128_CONTEXT *dest, SIV128_CONTEXT *src)
227
0
{
228
0
    memcpy(&dest->d, &src->d, sizeof(src->d));
229
0
    if (dest->cipher_ctx == NULL) {
230
0
        dest->cipher_ctx = EVP_CIPHER_CTX_new();
231
0
        if (dest->cipher_ctx == NULL)
232
0
            return 0;
233
0
    }
234
0
    if (!EVP_CIPHER_CTX_copy(dest->cipher_ctx, src->cipher_ctx))
235
0
        return 0;
236
0
    EVP_MAC_CTX_free(dest->mac_ctx_init);
237
0
    dest->mac_ctx_init = EVP_MAC_CTX_dup(src->mac_ctx_init);
238
0
    if (dest->mac_ctx_init == NULL)
239
0
        return 0;
240
0
    dest->mac = src->mac;
241
0
    if (dest->mac != NULL)
242
0
        EVP_MAC_up_ref(dest->mac);
243
0
    return 1;
244
0
}
245
246
/*
247
 * Provide any AAD. This can be called multiple times.
248
 * Per RFC5297, the last piece of associated data
249
 * is the nonce, but it's not treated special
250
 */
251
int ossl_siv128_aad(SIV128_CONTEXT *ctx, const unsigned char *aad,
252
    size_t len)
253
0
{
254
0
    SIV_BLOCK mac_out;
255
0
    size_t out_len = SIV_LEN;
256
0
    EVP_MAC_CTX *mac_ctx;
257
258
0
    siv128_dbl(&ctx->d);
259
260
0
    if ((mac_ctx = EVP_MAC_CTX_dup(ctx->mac_ctx_init)) == NULL
261
0
        || !EVP_MAC_update(mac_ctx, aad, len)
262
0
        || !EVP_MAC_final(mac_ctx, mac_out.byte, &out_len,
263
0
            sizeof(mac_out.byte))
264
0
        || out_len != SIV_LEN) {
265
0
        EVP_MAC_CTX_free(mac_ctx);
266
0
        return 0;
267
0
    }
268
0
    EVP_MAC_CTX_free(mac_ctx);
269
270
0
    siv128_xorblock(&ctx->d, &mac_out);
271
272
0
    return 1;
273
0
}
274
275
/*
276
 * Provide any data to be encrypted. This can be called once.
277
 */
278
int ossl_siv128_encrypt(SIV128_CONTEXT *ctx,
279
    const unsigned char *in, unsigned char *out,
280
    size_t len)
281
0
{
282
0
    SIV_BLOCK q;
283
0
    int ret = 0;
284
0
    int final_ret_value = -1;
285
286
    /* can only do one crypto operation */
287
0
    if (ctx->crypto_ok == 0)
288
0
        goto end;
289
0
    ctx->crypto_ok--;
290
291
0
    if (!siv128_do_s2v_p(ctx, &q, in, len))
292
0
        goto end;
293
294
0
    memcpy(ctx->tag.byte, &q, SIV_LEN);
295
0
    q.byte[8] &= 0x7f;
296
0
    q.byte[12] &= 0x7f;
297
298
0
    if (!siv128_do_encrypt(ctx->cipher_ctx, out, in, len, &q))
299
0
        goto end;
300
301
0
    ret = 1;
302
0
    final_ret_value = 0;
303
0
end:
304
0
    ctx->final_ret = final_ret_value;
305
0
    return ret;
306
0
}
307
308
/*
309
 * Provide any data to be decrypted. This can be called once.
310
 */
311
int ossl_siv128_decrypt(SIV128_CONTEXT *ctx,
312
    const unsigned char *in, unsigned char *out,
313
    size_t len)
314
0
{
315
0
    unsigned char *p;
316
0
    SIV_BLOCK t, q;
317
0
    int i;
318
0
    int ret = 0;
319
0
    int final_ret_value = -1;
320
321
    /* can only do one crypto operation */
322
0
    if (ctx->crypto_ok == 0)
323
0
        goto end;
324
0
    ctx->crypto_ok--;
325
326
0
    memcpy(&q, ctx->tag.byte, SIV_LEN);
327
0
    q.byte[8] &= 0x7f;
328
0
    q.byte[12] &= 0x7f;
329
330
0
    if (!siv128_do_encrypt(ctx->cipher_ctx, out, in, len, &q)
331
0
        || !siv128_do_s2v_p(ctx, &t, out, len))
332
0
        goto end;
333
334
0
    p = ctx->tag.byte;
335
0
    for (i = 0; i < SIV_LEN; i++)
336
0
        t.byte[i] ^= p[i];
337
338
0
    if ((t.word[0] | t.word[1]) != 0) {
339
0
        OPENSSL_cleanse(out, len);
340
0
        goto end;
341
0
    }
342
0
    ret = 1;
343
0
    final_ret_value = 0;
344
0
end:
345
0
    ctx->final_ret = final_ret_value;
346
0
    return ret;
347
0
}
348
349
/*
350
 * Return the already calculated final result.
351
 */
352
int ossl_siv128_finish(SIV128_CONTEXT *ctx)
353
0
{
354
0
    return ctx->final_ret;
355
0
}
356
357
/*
358
 * Set the tag
359
 */
360
int ossl_siv128_set_tag(SIV128_CONTEXT *ctx, const unsigned char *tag, size_t len)
361
0
{
362
0
    if (len != SIV_LEN)
363
0
        return 0;
364
365
    /* Copy the tag from the supplied buffer */
366
0
    memcpy(ctx->tag.byte, tag, len);
367
0
    return 1;
368
0
}
369
370
/*
371
 * Retrieve the calculated tag
372
 */
373
int ossl_siv128_get_tag(SIV128_CONTEXT *ctx, unsigned char *tag, size_t len)
374
0
{
375
0
    if (len != SIV_LEN)
376
0
        return 0;
377
378
    /* Copy the tag into the supplied buffer */
379
0
    memcpy(tag, ctx->tag.byte, len);
380
0
    return 1;
381
0
}
382
383
/*
384
 * Release all resources
385
 */
386
int ossl_siv128_cleanup(SIV128_CONTEXT *ctx)
387
0
{
388
0
    if (ctx != NULL) {
389
0
        EVP_CIPHER_CTX_free(ctx->cipher_ctx);
390
0
        ctx->cipher_ctx = NULL;
391
0
        EVP_MAC_CTX_free(ctx->mac_ctx_init);
392
0
        ctx->mac_ctx_init = NULL;
393
0
        EVP_MAC_free(ctx->mac);
394
0
        ctx->mac = NULL;
395
0
        OPENSSL_cleanse(&ctx->d, sizeof(ctx->d));
396
0
        OPENSSL_cleanse(&ctx->tag, sizeof(ctx->tag));
397
0
        ctx->final_ret = -1;
398
0
        ctx->crypto_ok = 1;
399
0
    }
400
0
    return 1;
401
0
}
402
403
int ossl_siv128_speed(SIV128_CONTEXT *ctx, int arg)
404
0
{
405
0
    ctx->crypto_ok = (arg == 1) ? -1 : 1;
406
0
    return 1;
407
0
}
408
409
#endif /* OPENSSL_NO_SIV */