Botan 3.13.0
Crypto and TLS for C&
siv.cpp
Go to the documentation of this file.
1/*
2* SIV Mode Encryption
3* (C) 2013,2017 Jack Lloyd
4* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/internal/siv.h>
10
11#include <botan/block_cipher.h>
12#include <botan/exceptn.h>
13#include <botan/mem_ops.h>
14#include <botan/internal/cmac.h>
15#include <botan/internal/ct_utils.h>
16#include <botan/internal/ctr.h>
17#include <botan/internal/int_utils.h>
18#include <botan/internal/poly_dbl.h>
19
20namespace Botan {
21
22SIV_Mode::SIV_Mode(std::unique_ptr<BlockCipher> cipher) :
23 m_name(cipher->name() + "/SIV"),
24 m_bs(cipher->block_size()),
25 m_ctr(std::make_unique<CTR_BE>(cipher->new_object(), 8)),
26 m_mac(std::make_unique<CMAC>(std::move(cipher))) {
27 // Not really true but only 128 bit allowed at the moment
28 if(m_bs != 16) {
29 throw Invalid_Argument("SIV requires a 128 bit block cipher");
30 }
31}
32
33SIV_Mode::~SIV_Mode() = default;
34
36 m_ctr->clear();
37 m_mac->clear();
38 m_ad_macs.clear();
39 reset();
40}
41
43 m_nonce.clear();
44 m_msg_buf.clear();
45 m_in_msg = false;
46}
47
48std::string SIV_Mode::name() const {
49 return m_name;
50}
51
52bool SIV_Mode::valid_nonce_length(size_t /*length*/) const {
53 return true;
54}
55
57 return 1;
58}
59
61 // Completely arbitrary value:
62 return 128;
63}
64
66 return true;
67}
68
70 return m_mac->key_spec().multiple(2);
71}
72
74 return m_ctr->has_keying_material() && m_mac->has_keying_material();
75}
76
77void SIV_Mode::key_schedule(std::span<const uint8_t> key) {
78 const size_t keylen = key.size() / 2;
79 m_mac->set_key(key.first(keylen));
80 m_ctr->set_key(key.last(keylen));
81 m_ad_macs.clear();
82 reset();
83}
84
86 return block_size() * 8 - 2;
87}
88
89void SIV_Mode::set_associated_data_n(size_t n, std::span<const uint8_t> ad) {
90 BOTAN_STATE_CHECK(!m_in_msg);
91 const size_t max_ads = maximum_associated_data_inputs();
92 if(n >= max_ads) {
93 throw Invalid_Argument(name() + " allows no more than " + std::to_string(max_ads) + " ADs");
94 }
95
96 if(n > m_ad_macs.size()) {
97 // If we are potentially skipping over AD elements in a way that will
98 // create a gap, fill the gaps in with the mac of the empty string.
99 const auto empty_mac = m_mac->process(std::span<const uint8_t>{});
100 m_ad_macs.resize(n + 1, empty_mac);
101 } else if(n == m_ad_macs.size()) {
102 m_ad_macs.resize(n + 1);
103 }
104
105 m_ad_macs[n] = m_mac->process(ad);
106}
107
108void SIV_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
109 BOTAN_STATE_CHECK(!m_in_msg);
110
111 if(!valid_nonce_length(nonce_len)) {
112 throw Invalid_IV_Length(name(), nonce_len);
113 }
114
115 if(nonce_len > 0) {
116 m_nonce = m_mac->process(nonce, nonce_len);
117 } else {
118 m_nonce.clear();
119 }
120
121 m_msg_buf.clear();
122 m_in_msg = true;
123}
124
125size_t SIV_Mode::process_msg(uint8_t buf[], size_t sz) {
126 // all output is saved for processing in finish
127 m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
128 // SIV supports a "no start_msg" mode (deterministic, nonce-less): the
129 // first process_msg locks AD just as start_msg would.
130 m_in_msg = true;
131 return 0;
132}
133
134secure_vector<uint8_t> SIV_Mode::S2V(const uint8_t* text, size_t text_len) {
135 // S2V processes at most block_size()*8 - 1 (127 for a 128-bit block)
136 // components; the associated data, the nonce (if present), and the plaintext
137 // are all components, so reject inputs that would exceed the limit.
138 const size_t s2v_components = m_ad_macs.size() + (m_nonce.empty() ? 0 : 1) + 1;
139 if(s2v_components > block_size() * 8 - 1) {
140 throw Invalid_Argument(name() + ": too many S2V components");
141 }
142
143 const std::vector<uint8_t> zeros(block_size());
144
145 secure_vector<uint8_t> V = m_mac->process(zeros.data(), zeros.size());
146
147 for(const auto& ad_mac : m_ad_macs) {
148 poly_double_n(V.data(), V.size());
149 V ^= ad_mac;
150 }
151
152 if(!m_nonce.empty()) {
153 poly_double_n(V.data(), V.size());
154 V ^= m_nonce;
155 }
156
157 if(text_len < block_size()) {
158 poly_double_n(V.data(), V.size());
159 xor_buf(V.data(), text, text_len);
160 V[text_len] ^= 0x80;
161 return m_mac->process(V);
162 }
163
164 m_mac->update(text, text_len - block_size());
165 xor_buf(V.data(), &text[text_len - block_size()], block_size());
166 m_mac->update(V);
167
168 return m_mac->final();
169}
170
172 V[m_bs - 8] &= 0x7F;
173 V[m_bs - 4] &= 0x7F;
174
175 ctr().set_iv(V.data(), V.size());
176}
177
178size_t SIV_Encryption::output_length(size_t input_length) const {
179 return add_or_throw(input_length, tag_size(), "SIV input too large");
180}
181
182void SIV_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
183 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
184
185 buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
186
187 const secure_vector<uint8_t> V = S2V(buffer.data() + offset, buffer.size() - offset);
188
189 buffer.insert(buffer.begin() + offset, V.begin(), V.end());
190
191 if(buffer.size() != offset + V.size()) {
192 set_ctr_iv(V);
193 ctr().cipher1(&buffer[offset + V.size()], buffer.size() - offset - V.size());
194 }
195
196 // Drop m_nonce as well as the in-message flag. S2V() consumes m_nonce,
197 // so leaving it live would let a subsequent finish_msg() without an
198 // intervening start_msg() silently re-use the prior nonce instead of
199 // running nonce-less SIV.
200 reset();
201}
202
203size_t SIV_Decryption::output_length(size_t input_length) const {
204 BOTAN_ARG_CHECK(input_length >= tag_size(), "Message too short to be valid");
205 return input_length - tag_size();
206}
207
208void SIV_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
209 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
210
211 if(!msg_buf().empty()) {
212 buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
213 msg_buf().clear();
214 }
215
216 const size_t sz = buffer.size() - offset;
217
218 BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
219
220 secure_vector<uint8_t> V(buffer.data() + offset, buffer.data() + offset + block_size());
221
222 if(buffer.size() != offset + V.size()) {
223 set_ctr_iv(V);
224
225 ctr().cipher(buffer.data() + offset + V.size(), buffer.data() + offset, buffer.size() - offset - V.size());
226 }
227
228 const secure_vector<uint8_t> T = S2V(buffer.data() + offset, buffer.size() - offset - V.size());
229
230 // See SIV_Encryption::finish_msg for why this is reset() and not just
231 // clearing the in-message flag.
232 reset();
233
234 if(!CT::is_equal<uint8_t>(T, V).as_bool()) {
235 clear_mem(std::span{buffer}.subspan(offset, buffer.size() - offset - V.size()));
236 throw Invalid_Authentication_Tag("SIV tag check failed");
237 }
238
239 buffer.resize(buffer.size() - tag_size());
240}
241
242} // namespace Botan
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:49
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
Key_Length_Specification multiple(size_t n) const
Definition sym_algo.h:72
size_t output_length(size_t input_length) const override
Definition siv.cpp:203
size_t output_length(size_t input_length) const override
Definition siv.cpp:178
void clear() final
Definition siv.cpp:35
size_t block_size() const
Definition siv.h:61
bool requires_entire_message() const final
Definition siv.cpp:65
SIV_Mode(std::unique_ptr< BlockCipher > cipher)
Definition siv.cpp:22
void set_ctr_iv(secure_vector< uint8_t > V)
Definition siv.cpp:171
secure_vector< uint8_t > S2V(const uint8_t text[], size_t text_len)
Definition siv.cpp:134
StreamCipher & ctr()
Definition siv.h:63
size_t tag_size() const final
Definition siv.h:52
size_t ideal_granularity() const final
Definition siv.cpp:60
size_t update_granularity() const final
Definition siv.cpp:56
bool valid_nonce_length(size_t length) const final
Definition siv.cpp:52
std::string name() const final
Definition siv.cpp:48
~SIV_Mode() override
secure_vector< uint8_t > & msg_buf()
Definition siv.h:67
bool has_keying_material() const final
Definition siv.cpp:73
size_t maximum_associated_data_inputs() const final
Definition siv.cpp:85
void reset() final
Definition siv.cpp:42
Key_Length_Specification key_spec() const final
Definition siv.cpp:69
void set_associated_data_n(size_t n, std::span< const uint8_t > ad) final
Definition siv.cpp:89
void cipher1(uint8_t buf[], size_t len)
void set_iv(const uint8_t iv[], size_t iv_len)
void cipher(const uint8_t in[], uint8_t out[], size_t len)
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:798
constexpr T add_or_throw(T a, T b, std::string_view msg)
Definition int_utils.h:66
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:403
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
void poly_double_n(uint8_t out[], const uint8_t in[], size_t n)
Definition poly_dbl.cpp:81
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:118