123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665 |
- /* cipher-gcm-siv.c - GCM-SIV implementation (RFC 8452)
- * Copyright (C) 2021 Jussi Kivilinna <jussi.kivilinna@iki.fi>
- *
- * This file is part of Libgcrypt.
- *
- * Libgcrypt is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * Libgcrypt 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
- #include <config.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include "g10lib.h"
- #include "cipher.h"
- #include "bufhelp.h"
- #include "./cipher-internal.h"
- #define GCM_SIV_NONCE_LENGTH (96 / 8)
- static inline void
- mulx_ghash (byte *a)
- {
- u64 t[2], mask;
- t[0] = buf_get_be64(a + 0);
- t[1] = buf_get_be64(a + 8);
- mask = -(t[1] & 1) & 0xe1;
- mask <<= 56;
- buf_put_be64(a + 8, (t[1] >> 1) ^ (t[0] << 63));
- buf_put_be64(a + 0, (t[0] >> 1) ^ mask);
- }
- static inline void
- gcm_siv_bytecounter_add (u32 ctr[2], size_t add)
- {
- if (sizeof(add) > sizeof(u32))
- {
- u32 high_add = ((add >> 31) >> 1) & 0xffffffff;
- ctr[1] += high_add;
- }
- ctr[0] += add;
- if (ctr[0] >= add)
- return;
- ++ctr[1];
- }
- static inline int
- gcm_siv_check_len (u32 ctr[2])
- {
- /* len(plaintext/aadlen) <= 2^39-256 bits == 2^36-32 bytes == 2^32-2 blocks */
- if (ctr[1] > 0xfU)
- return 0;
- if (ctr[1] < 0xfU)
- return 1;
- if (ctr[0] <= 0xffffffe0U)
- return 1;
- return 0;
- }
- static void
- polyval_set_key (gcry_cipher_hd_t c, const byte *auth_key)
- {
- cipher_block_bswap (c->u_mode.gcm.u_ghash_key.key, auth_key,
- GCRY_SIV_BLOCK_LEN);
- mulx_ghash (c->u_mode.gcm.u_ghash_key.key);
- _gcry_cipher_gcm_setupM (c);
- }
- static void
- do_polyval_buf(gcry_cipher_hd_t c, byte *hash, const byte *buf,
- size_t buflen, int do_padding)
- {
- unsigned int blocksize = GCRY_SIV_BLOCK_LEN;
- unsigned int unused = c->u_mode.gcm.mac_unused;
- ghash_fn_t ghash_fn = c->u_mode.gcm.ghash_fn;
- ghash_fn_t polyval_fn = c->u_mode.gcm.polyval_fn;
- byte tmp_blocks[16][GCRY_SIV_BLOCK_LEN];
- size_t nblocks, n;
- unsigned int burn = 0, nburn;
- unsigned int num_blks_used = 0;
- if (buflen == 0 && (unused == 0 || !do_padding))
- return;
- do
- {
- if (buflen > 0 && (buflen + unused < blocksize || unused > 0))
- {
- n = blocksize - unused;
- n = n < buflen ? n : buflen;
- buf_cpy (&c->u_mode.gcm.macbuf[unused], buf, n);
- unused += n;
- buf += n;
- buflen -= n;
- }
- if (!buflen)
- {
- if (!do_padding && unused < blocksize)
- {
- break;
- }
- n = blocksize - unused;
- if (n > 0)
- {
- memset (&c->u_mode.gcm.macbuf[unused], 0, n);
- unused = blocksize;
- }
- }
- if (unused > 0)
- {
- gcry_assert (unused == blocksize);
- /* Process one block from macbuf. */
- if (polyval_fn)
- {
- nburn = polyval_fn (c, hash, c->u_mode.gcm.macbuf, 1);
- }
- else
- {
- cipher_block_bswap (c->u_mode.gcm.macbuf, c->u_mode.gcm.macbuf,
- blocksize);
- nburn = ghash_fn (c, hash, c->u_mode.gcm.macbuf, 1);
- }
- burn = nburn > burn ? nburn : burn;
- unused = 0;
- }
- nblocks = buflen / blocksize;
- while (nblocks)
- {
- if (polyval_fn)
- {
- n = nblocks;
- nburn = polyval_fn (c, hash, buf, n);
- }
- else
- {
- for (n = 0; n < (nblocks > 16 ? 16 : nblocks); n++)
- cipher_block_bswap (tmp_blocks[n], buf + n * blocksize,
- blocksize);
- num_blks_used = n > num_blks_used ? n : num_blks_used;
- nburn = ghash_fn (c, hash, tmp_blocks[0], n);
- }
- burn = nburn > burn ? nburn : burn;
- buf += n * blocksize;
- buflen -= n * blocksize;
- nblocks -= n;
- }
- }
- while (buflen > 0);
- c->u_mode.gcm.mac_unused = unused;
- if (num_blks_used)
- wipememory (tmp_blocks, num_blks_used * blocksize);
- if (burn)
- _gcry_burn_stack (burn);
- }
- static void
- do_ctr_le32 (gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf,
- size_t inbuflen)
- {
- gcry_cipher_encrypt_t enc_fn = c->spec->encrypt;
- unsigned char tmp[GCRY_SIV_BLOCK_LEN];
- unsigned int burn = 0, nburn;
- size_t nblocks;
- if (inbuflen == 0)
- return;
- /* Use a bulk method if available. */
- nblocks = inbuflen / GCRY_SIV_BLOCK_LEN;
- if (nblocks && c->bulk.ctr32le_enc)
- {
- c->bulk.ctr32le_enc (c->context.c, c->u_ctr.ctr, outbuf, inbuf, nblocks);
- inbuf += nblocks * GCRY_SIV_BLOCK_LEN;
- outbuf += nblocks * GCRY_SIV_BLOCK_LEN;
- inbuflen -= nblocks * GCRY_SIV_BLOCK_LEN;
- }
- do
- {
- nburn = enc_fn (c->context.c, tmp, c->u_ctr.ctr);
- burn = nburn > burn ? nburn : burn;
- buf_put_le32(c->u_ctr.ctr, buf_get_le32(c->u_ctr.ctr) + 1);
- if (inbuflen < GCRY_SIV_BLOCK_LEN)
- break;
- cipher_block_xor(outbuf, inbuf, tmp, GCRY_SIV_BLOCK_LEN);
- inbuflen -= GCRY_SIV_BLOCK_LEN;
- outbuf += GCRY_SIV_BLOCK_LEN;
- inbuf += GCRY_SIV_BLOCK_LEN;
- }
- while (inbuflen);
- if (inbuflen)
- {
- buf_xor(outbuf, inbuf, tmp, inbuflen);
- outbuf += inbuflen;
- inbuf += inbuflen;
- inbuflen -= inbuflen;
- }
- wipememory (tmp, sizeof(tmp));
- if (burn > 0)
- _gcry_burn_stack (burn + 4 * sizeof(void *));
- }
- static int
- gcm_siv_selftest (gcry_cipher_hd_t c)
- {
- static const byte in1[GCRY_SIV_BLOCK_LEN] =
- "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
- static const byte out1[GCRY_SIV_BLOCK_LEN] =
- "\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
- static const byte in2[GCRY_SIV_BLOCK_LEN] =
- "\x9c\x98\xc0\x4d\xf9\x38\x7d\xed\x82\x81\x75\xa9\x2b\xa6\x52\xd8";
- static const byte out2[GCRY_SIV_BLOCK_LEN] =
- "\x4e\x4c\x60\x26\xfc\x9c\x3e\xf6\xc1\x40\xba\xd4\x95\xd3\x29\x6c";
- static const byte polyval_key[GCRY_SIV_BLOCK_LEN] =
- "\x25\x62\x93\x47\x58\x92\x42\x76\x1d\x31\xf8\x26\xba\x4b\x75\x7b";
- static const byte ghash_key[GCRY_SIV_BLOCK_LEN] =
- "\xdc\xba\xa5\xdd\x13\x7c\x18\x8e\xbb\x21\x49\x2c\x23\xc9\xb1\x12";
- static const byte polyval_data[GCRY_SIV_BLOCK_LEN * 2] =
- "\x4f\x4f\x95\x66\x8c\x83\xdf\xb6\x40\x17\x62\xbb\x2d\x01\xa2\x62"
- "\xd1\xa2\x4d\xdd\x27\x21\xd0\x06\xbb\xe4\x5f\x20\xd3\xc9\xf3\x62";
- static const byte polyval_tag[GCRY_SIV_BLOCK_LEN] =
- "\xf7\xa3\xb4\x7b\x84\x61\x19\xfa\xe5\xb7\x86\x6c\xf5\xe5\xb7\x7e";
- byte tmp[GCRY_SIV_BLOCK_LEN];
- /* Test mulx_ghash */
- memcpy (tmp, in1, GCRY_SIV_BLOCK_LEN);
- mulx_ghash (tmp);
- if (memcmp (tmp, out1, GCRY_SIV_BLOCK_LEN) != 0)
- return -1;
- memcpy (tmp, in2, GCRY_SIV_BLOCK_LEN);
- mulx_ghash (tmp);
- if (memcmp (tmp, out2, GCRY_SIV_BLOCK_LEN) != 0)
- return -1;
- /* Test GHASH key generation */
- memcpy (tmp, polyval_key, GCRY_SIV_BLOCK_LEN);
- cipher_block_bswap (tmp, tmp, GCRY_SIV_BLOCK_LEN);
- mulx_ghash (tmp);
- if (memcmp (tmp, ghash_key, GCRY_SIV_BLOCK_LEN) != 0)
- return -1;
- /* Test POLYVAL */
- memset (&c->u_mode.gcm, 0, sizeof(c->u_mode.gcm));
- polyval_set_key (c, polyval_key);
- memset (&tmp, 0, sizeof(tmp));
- do_polyval_buf (c, tmp, polyval_data, GCRY_SIV_BLOCK_LEN * 2, 1);
- cipher_block_bswap (tmp, tmp, GCRY_SIV_BLOCK_LEN);
- if (memcmp (tmp, polyval_tag, GCRY_SIV_BLOCK_LEN) != 0)
- return -1;
- return 0;
- }
- gcry_err_code_t
- _gcry_cipher_gcm_siv_setkey (gcry_cipher_hd_t c, unsigned int keylen)
- {
- static int done;
- if (keylen != 16 && keylen != 32)
- return GPG_ERR_INV_KEYLEN;
- if (!done)
- {
- if (gcm_siv_selftest (c))
- return GPG_ERR_SELFTEST_FAILED;
- done = 1;
- }
- c->marks.iv = 0;
- c->marks.tag = 0;
- memset (&c->u_mode.gcm, 0, sizeof(c->u_mode.gcm));
- c->u_mode.gcm.siv_keylen = keylen;
- return 0;
- }
- gcry_err_code_t
- _gcry_cipher_gcm_siv_set_nonce (gcry_cipher_hd_t c, const byte *iv,
- size_t ivlen)
- {
- byte auth_key[GCRY_SIV_BLOCK_LEN];
- byte tmp_in[GCRY_SIV_BLOCK_LEN];
- byte tmp[GCRY_SIV_BLOCK_LEN];
- byte enc_key[32];
- gcry_err_code_t err;
- if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN)
- return GPG_ERR_CIPHER_ALGO;
- if (ivlen != GCM_SIV_NONCE_LENGTH)
- return GPG_ERR_INV_ARG;
- if (c->u_mode.gcm.siv_keylen == 0)
- return GPG_ERR_INV_STATE;
- if (c->marks.iv)
- {
- /* If nonce is already set, use cipher_reset or setkey first to reset
- * cipher state. */
- return GPG_ERR_INV_STATE;
- }
- memset (c->u_mode.gcm.aadlen, 0, sizeof(c->u_mode.gcm.aadlen));
- memset (c->u_mode.gcm.datalen, 0, sizeof(c->u_mode.gcm.datalen));
- memset (c->u_mode.gcm.u_tag.tag, 0, sizeof(c->u_mode.gcm.u_tag.tag));
- c->u_mode.gcm.datalen_over_limits = 0;
- c->u_mode.gcm.ghash_data_finalized = 0;
- c->u_mode.gcm.ghash_aad_finalized = 0;
- memset (c->u_iv.iv, 0, GCRY_SIV_BLOCK_LEN);
- memcpy (c->u_iv.iv, iv, ivlen);
- memcpy (tmp_in + 4, iv, ivlen);
- /* Derive message authentication key */
- buf_put_le32(tmp_in, 0);
- c->spec->encrypt (&c->context.c, tmp, tmp_in);
- memcpy (auth_key + 0, tmp, 8);
- buf_put_le32(tmp_in, 1);
- c->spec->encrypt (&c->context.c, tmp, tmp_in);
- memcpy (auth_key + 8, tmp, 8);
- polyval_set_key (c, auth_key);
- wipememory (auth_key, sizeof(auth_key));
- /* Derive message encryption key */
- buf_put_le32(tmp_in, 2);
- c->spec->encrypt (&c->context.c, tmp, tmp_in);
- memcpy (enc_key + 0, tmp, 8);
- buf_put_le32(tmp_in, 3);
- c->spec->encrypt (&c->context.c, tmp, tmp_in);
- memcpy (enc_key + 8, tmp, 8);
- if (c->u_mode.gcm.siv_keylen >= 24)
- {
- buf_put_le32(tmp_in, 4);
- c->spec->encrypt (&c->context.c, tmp, tmp_in);
- memcpy (enc_key + 16, tmp, 8);
- }
- if (c->u_mode.gcm.siv_keylen >= 32)
- {
- buf_put_le32(tmp_in, 5);
- c->spec->encrypt (&c->context.c, tmp, tmp_in);
- memcpy (enc_key + 24, tmp, 8);
- }
- wipememory (tmp, sizeof(tmp));
- wipememory (tmp_in, sizeof(tmp_in));
- err = c->spec->setkey (&c->context.c, enc_key, c->u_mode.gcm.siv_keylen,
- &c->bulk);
- wipememory (enc_key, sizeof(enc_key));
- if (err)
- return err;
- c->marks.iv = 1;
- return 0;
- }
- gcry_err_code_t
- _gcry_cipher_gcm_siv_authenticate (gcry_cipher_hd_t c,
- const byte *aadbuf, size_t aadbuflen)
- {
- if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN)
- return GPG_ERR_CIPHER_ALGO;
- if (c->u_mode.gcm.datalen_over_limits)
- return GPG_ERR_INV_LENGTH;
- if (c->marks.tag
- || !c->marks.iv
- || c->u_mode.gcm.ghash_aad_finalized
- || c->u_mode.gcm.ghash_data_finalized
- || !c->u_mode.gcm.ghash_fn)
- return GPG_ERR_INV_STATE;
- gcm_siv_bytecounter_add (c->u_mode.gcm.aadlen, aadbuflen);
- if (!gcm_siv_check_len (c->u_mode.gcm.aadlen))
- {
- c->u_mode.gcm.datalen_over_limits = 1;
- return GPG_ERR_INV_LENGTH;
- }
- do_polyval_buf (c, c->u_mode.gcm.u_tag.tag, aadbuf, aadbuflen, 0);
- return 0;
- }
- gcry_err_code_t
- _gcry_cipher_gcm_siv_encrypt (gcry_cipher_hd_t c,
- byte *outbuf, size_t outbuflen,
- const byte *inbuf, size_t inbuflen)
- {
- u32 bitlengths[2][2];
- if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN)
- return GPG_ERR_CIPHER_ALGO;
- if (outbuflen < inbuflen)
- return GPG_ERR_BUFFER_TOO_SHORT;
- if (c->u_mode.gcm.datalen_over_limits)
- return GPG_ERR_INV_LENGTH;
- if (c->marks.tag
- || !c->marks.iv
- || c->u_mode.gcm.ghash_data_finalized
- || !c->u_mode.gcm.ghash_fn)
- return GPG_ERR_INV_STATE;
- if (!c->u_mode.gcm.ghash_aad_finalized)
- {
- /* Start of encryption marks end of AAD stream. */
- do_polyval_buf(c, c->u_mode.gcm.u_tag.tag, NULL, 0, 1);
- c->u_mode.gcm.ghash_aad_finalized = 1;
- }
- gcm_siv_bytecounter_add (c->u_mode.gcm.datalen, inbuflen);
- if (!gcm_siv_check_len (c->u_mode.gcm.datalen))
- {
- c->u_mode.gcm.datalen_over_limits = 1;
- return GPG_ERR_INV_LENGTH;
- }
- /* Plaintext and padding to POLYVAL. */
- do_polyval_buf (c, c->u_mode.gcm.u_tag.tag, inbuf, inbuflen, 1);
- c->u_mode.gcm.ghash_data_finalized = 1;
- /* aad length */
- bitlengths[0][0] = le_bswap32(c->u_mode.gcm.aadlen[0] << 3);
- bitlengths[0][1] = le_bswap32((c->u_mode.gcm.aadlen[0] >> 29) |
- (c->u_mode.gcm.aadlen[1] << 3));
- /* data length */
- bitlengths[1][0] = le_bswap32(c->u_mode.gcm.datalen[0] << 3);
- bitlengths[1][1] = le_bswap32((c->u_mode.gcm.datalen[0] >> 29) |
- (c->u_mode.gcm.datalen[1] << 3));
- /* Length block to POLYVAL. */
- do_polyval_buf(c, c->u_mode.gcm.u_tag.tag, (byte *)bitlengths,
- GCRY_SIV_BLOCK_LEN, 1);
- wipememory (bitlengths, sizeof(bitlengths));
- /* Prepare tag and counter. */
- cipher_block_bswap (c->u_mode.gcm.u_tag.tag, c->u_mode.gcm.u_tag.tag,
- GCRY_SIV_BLOCK_LEN);
- cipher_block_xor (c->u_mode.gcm.tagiv, c->u_iv.iv, c->u_mode.gcm.u_tag.tag,
- GCRY_SIV_BLOCK_LEN);
- c->u_mode.gcm.tagiv[GCRY_SIV_BLOCK_LEN - 1] &= 0x7f;
- c->spec->encrypt (&c->context.c, c->u_mode.gcm.tagiv, c->u_mode.gcm.tagiv);
- c->marks.tag = 1;
- memcpy (c->u_ctr.ctr, c->u_mode.gcm.tagiv, GCRY_SIV_BLOCK_LEN);
- c->u_ctr.ctr[GCRY_SIV_BLOCK_LEN - 1] |= 0x80;
- /* Encrypt data */
- do_ctr_le32 (c, outbuf, inbuf, inbuflen);
- return 0;
- }
- gcry_err_code_t
- _gcry_cipher_gcm_siv_set_decryption_tag (gcry_cipher_hd_t c,
- const byte *tag, size_t taglen)
- {
- if (taglen != GCRY_SIV_BLOCK_LEN)
- return GPG_ERR_INV_ARG;
- if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN)
- return GPG_ERR_CIPHER_ALGO;
- if (c->marks.tag)
- return GPG_ERR_INV_STATE;
- memcpy (c->u_mode.gcm.tagiv, tag, GCRY_SIV_BLOCK_LEN);
- c->marks.tag = 1;
- return 0;
- }
- gcry_err_code_t
- _gcry_cipher_gcm_siv_decrypt (gcry_cipher_hd_t c,
- byte *outbuf, size_t outbuflen,
- const byte *inbuf, size_t inbuflen)
- {
- byte expected_tag[GCRY_SIV_BLOCK_LEN];
- u32 bitlengths[2][2];
- gcry_err_code_t rc = 0;
- if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN)
- return GPG_ERR_CIPHER_ALGO;
- if (outbuflen < inbuflen)
- return GPG_ERR_BUFFER_TOO_SHORT;
- if (c->u_mode.gcm.datalen_over_limits)
- return GPG_ERR_INV_LENGTH;
- if (!c->marks.tag
- || !c->marks.iv
- || c->u_mode.gcm.ghash_data_finalized
- || !c->u_mode.gcm.ghash_fn)
- return GPG_ERR_INV_STATE;
- if (!c->u_mode.gcm.ghash_aad_finalized)
- {
- /* Start of encryption marks end of AAD stream. */
- do_polyval_buf(c, c->u_mode.gcm.u_tag.tag, NULL, 0, 1);
- c->u_mode.gcm.ghash_aad_finalized = 1;
- }
- gcm_siv_bytecounter_add (c->u_mode.gcm.datalen, inbuflen);
- if (!gcm_siv_check_len (c->u_mode.gcm.datalen))
- {
- c->u_mode.gcm.datalen_over_limits = 1;
- return GPG_ERR_INV_LENGTH;
- }
- /* Prepare counter. */
- memcpy (c->u_ctr.ctr, c->u_mode.gcm.tagiv, GCRY_SIV_BLOCK_LEN);
- c->u_ctr.ctr[GCRY_SIV_BLOCK_LEN - 1] |= 0x80;
- /* Decrypt data. */
- do_ctr_le32 (c, outbuf, inbuf, inbuflen);
- /* Plaintext and padding to POLYVAL. */
- do_polyval_buf (c, c->u_mode.gcm.u_tag.tag, outbuf, inbuflen, 1);
- c->u_mode.gcm.ghash_data_finalized = 1;
- /* aad length */
- bitlengths[0][0] = le_bswap32(c->u_mode.gcm.aadlen[0] << 3);
- bitlengths[0][1] = le_bswap32((c->u_mode.gcm.aadlen[0] >> 29) |
- (c->u_mode.gcm.aadlen[1] << 3));
- /* data length */
- bitlengths[1][0] = le_bswap32(c->u_mode.gcm.datalen[0] << 3);
- bitlengths[1][1] = le_bswap32((c->u_mode.gcm.datalen[0] >> 29) |
- (c->u_mode.gcm.datalen[1] << 3));
- /* Length block to POLYVAL. */
- do_polyval_buf(c, c->u_mode.gcm.u_tag.tag, (byte *)bitlengths,
- GCRY_SIV_BLOCK_LEN, 1);
- wipememory (bitlengths, sizeof(bitlengths));
- /* Prepare tag. */
- cipher_block_bswap (c->u_mode.gcm.u_tag.tag, c->u_mode.gcm.u_tag.tag,
- GCRY_SIV_BLOCK_LEN);
- cipher_block_xor (expected_tag, c->u_iv.iv, c->u_mode.gcm.u_tag.tag,
- GCRY_SIV_BLOCK_LEN);
- expected_tag[GCRY_SIV_BLOCK_LEN - 1] &= 0x7f;
- c->spec->encrypt (&c->context.c, expected_tag, expected_tag);
- if (!buf_eq_const(c->u_mode.gcm.tagiv, expected_tag, GCRY_SIV_BLOCK_LEN))
- {
- wipememory (outbuf, inbuflen);
- rc = GPG_ERR_CHECKSUM;
- }
- wipememory (expected_tag, sizeof(expected_tag));
- return rc;
- }
- static gcry_err_code_t
- _gcry_cipher_gcm_siv_tag (gcry_cipher_hd_t c,
- byte * outbuf, size_t outbuflen, int check)
- {
- gcry_err_code_t err;
- if (!c->marks.tag)
- {
- if (!c->u_mode.gcm.ghash_fn)
- return GPG_ERR_INV_STATE;
- if (!c->marks.tag)
- {
- /* Finalize GCM-SIV with zero-length plaintext. */
- err = _gcry_cipher_gcm_siv_encrypt (c, NULL, 0, NULL, 0);
- if (err != 0)
- return err;
- }
- }
- if (c->u_mode.gcm.datalen_over_limits)
- return GPG_ERR_INV_LENGTH;
- if (!c->u_mode.gcm.ghash_data_finalized)
- return GPG_ERR_INV_STATE;
- if (!c->marks.tag)
- return GPG_ERR_INV_STATE;
- if (!check)
- {
- if (outbuflen > GCRY_SIV_BLOCK_LEN)
- outbuflen = GCRY_SIV_BLOCK_LEN;
- /* NB: We already checked that OUTBUF is large enough to hold
- * the result or has valid truncated length. */
- memcpy (outbuf, c->u_mode.gcm.tagiv, outbuflen);
- }
- else
- {
- /* OUTBUFLEN gives the length of the user supplied tag in OUTBUF
- * and thus we need to compare its length first. */
- if (outbuflen != GCRY_SIV_BLOCK_LEN
- || !buf_eq_const (outbuf, c->u_mode.gcm.tagiv, outbuflen))
- return GPG_ERR_CHECKSUM;
- }
- return 0;
- }
- gcry_err_code_t
- _gcry_cipher_gcm_siv_get_tag (gcry_cipher_hd_t c, unsigned char *outtag,
- size_t taglen)
- {
- return _gcry_cipher_gcm_siv_tag (c, outtag, taglen, 0);
- }
- gcry_err_code_t
- _gcry_cipher_gcm_siv_check_tag (gcry_cipher_hd_t c,
- const unsigned char *intag,
- size_t taglen)
- {
- return _gcry_cipher_gcm_siv_tag (c, (unsigned char *)intag, taglen, 1);
- }
|