8#include <botan/internal/gcm_siv.h>
10#include <botan/exceptn.h>
11#include <botan/mem_ops.h>
12#include <botan/internal/ct_utils.h>
13#include <botan/internal/fmt.h>
14#include <botan/internal/int_utils.h>
15#include <botan/internal/loadstor.h>
22 if(cipher.block_size() != 16) {
29 const bool k32 = cipher.valid_keylength(32);
38 throw Invalid_Argument(
"GCM-SIV requires a cipher supporting 128 or 256 bit keys");
45 m_cipher_name(cipher->
name()),
46 m_key_spec(gcm_siv_key_spec(*cipher)),
47 m_cipher(std::move(cipher)),
48 m_msg_cipher(m_cipher->new_object()) {}
61 m_msg_cipher->clear();
69 return fmt(
"{}/GCM-SIV", m_cipher_name);
73 return m_polyval.provider();
93 return m_cipher->has_keying_material();
96void GCM_SIV_Mode::key_schedule(std::span<const uint8_t> key) {
97 m_cipher->set_key(key);
98 m_kgk_len = key.size();
103 BOTAN_ARG_CHECK(idx == 0,
"GCM-SIV: cannot handle non-zero index in set_associated_data_n");
106 m_ad.assign(ad.begin(), ad.end());
109void GCM_SIV_Mode::start_msg(
const uint8_t nonce[],
size_t nonce_len) {
117 copy_mem(m_nonce, std::span{nonce, nonce_len});
126 const size_t blocks = (m_kgk_len == 16) ? 4 : 6;
128 std::array<uint8_t, 6 * BS> kb{};
129 for(
size_t i = 0; i != blocks; ++i) {
130 store_le(
static_cast<uint32_t
>(i), &kb[
BS * i]);
131 copy_mem(&kb[
BS * i + 4], m_nonce.data(), m_nonce.size());
133 m_cipher->encrypt_n(kb.data(), kb.data(), blocks);
135 std::array<uint8_t, 16> auth_key{};
138 for(
size_t i = 0; i != 2; ++i) {
141 for(
size_t i = 0; i != blocks - 2; ++i) {
142 copy_mem(&enc_key[8 * i], &kb[
BS * (i + 2)], 8);
145 m_polyval.set_key(auth_key);
146 m_msg_cipher->set_key(enc_key);
155size_t GCM_SIV_Mode::process_msg(uint8_t buf[],
size_t sz) {
161 throw Invalid_State(
"GCM-SIV message length limit exceeded");
165 m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
170 m_polyval.update(m_ad);
171 m_polyval.zero_pad();
172 m_polyval.update(ptext);
173 m_polyval.zero_pad();
175 const uint64_t ad_bits = 8 *
static_cast<uint64_t
>(m_ad.size());
176 const uint64_t pt_bits = 8 *
static_cast<uint64_t
>(ptext.size());
177 m_polyval.update(
store_le(ad_bits, pt_bits));
179 std::array<uint8_t, BS> S{};
187 xor_buf(S.data(), m_nonce.data(), m_nonce.size());
189 m_msg_cipher->encrypt(S);
201 std::array<uint8_t, BS> ctr_block{};
203 ctr_block[15] |= 0x80;
210 const size_t blocks = std::min((len +
BS - 1) /
BS, ks.size() /
BS);
212 for(
size_t i = 0; i != blocks; ++i) {
218 m_msg_cipher->encrypt_n(ks.data(), ks.data(), blocks);
220 const size_t todo = std::min(len, blocks *
BS);
235 buffer.insert(buffer.begin() + offset,
msg_buf().begin(),
msg_buf().end());
238 const size_t ptext_len = buffer.size() - offset;
240 uint8_t* buf = buffer.data() + offset;
245 buffer += std::make_pair(tag.data(), tag.size());
259 buffer.insert(buffer.begin() + offset,
msg_buf().begin(),
msg_buf().end());
263 const size_t sz = buffer.size() - offset;
266 const size_t ctext_len = sz -
tag_size();
268 uint8_t* buf = buffer.data() + offset;
270 std::array<uint8_t, 16> included_tag{};
271 copy_mem(included_tag, std::span{buf + ctext_len, 16});
273 ctr_xor(included_tag, buf, ctext_len);
275 const auto expected_tag =
compute_tag({buf, ctext_len});
279 if(!
CT::is_equal(expected_tag.data(), included_tag.data(), included_tag.size()).as_bool()) {
280 clear_mem(std::span{buffer}.subspan(offset, ctext_len));
281 throw Invalid_Authentication_Tag(
"GCM-SIV tag check failed");
284 buffer.resize(offset + ctext_len);
#define BOTAN_STATE_CHECK(expr)
#define BOTAN_ARG_CHECK(expr, msg)
static constexpr size_t ParallelismMult
size_t output_length(size_t input_length) const override
size_t output_length(size_t input_length) const override
std::string name() const final
static constexpr size_t BS
static constexpr uint64_t MAX_INPUT_LEN
RFC 8452 limits both the plaintext and the AD to 2**36 bytes.
bool valid_nonce_length(size_t len) const final
std::string provider() const final
size_t ideal_granularity() const final
void ctr_xor(std::span< const uint8_t, BS > tag, uint8_t buf[], size_t len)
XOR the buffer with the CTR keystream, starting from the tag-derived counter.
size_t tag_size() const final
Key_Length_Specification key_spec() const final
bool has_keying_material() const final
secure_vector< uint8_t > & msg_buf()
size_t update_granularity() const final
void set_associated_data_n(size_t idx, std::span< const uint8_t > ad) final
GCM_SIV_Mode(std::unique_ptr< BlockCipher > cipher)
std::array< uint8_t, BS > compute_tag(std::span< const uint8_t > ptext)
Compute the expected tag for the (unpadded) plaintext.
bool valid_keylength(size_t length) const
void assert_key_material_set() const
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
constexpr T add_or_throw(T a, T b, std::string_view msg)
void zap(std::vector< T, Alloc > &vec)
std::string fmt(std::string_view format, const T &... args)
constexpr void copy_mem(T *out, const T *in, size_t n)
void secure_scrub_memory(void *ptr, size_t n)
constexpr auto store_le(ParamTs &&... params)
constexpr auto load_le(ParamTs &&... params)
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
std::vector< T, secure_allocator< T > > secure_vector
constexpr void clear_mem(T *ptr, size_t n)