Coverage Report

Created: 2025-04-11 06:34

/src/botan/src/lib/modes/aead/siv/siv.cpp
Line
Count
Source (jump to first uncovered line)
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/mem_ops.h>
13
#include <botan/internal/cmac.h>
14
#include <botan/internal/ct_utils.h>
15
#include <botan/internal/ctr.h>
16
#include <botan/internal/poly_dbl.h>
17
18
namespace Botan {
19
20
SIV_Mode::SIV_Mode(std::unique_ptr<BlockCipher> cipher) :
21
0
      m_name(cipher->name() + "/SIV"),
22
0
      m_bs(cipher->block_size()),
23
0
      m_ctr(std::make_unique<CTR_BE>(cipher->new_object(), 8)),
24
0
      m_mac(std::make_unique<CMAC>(std::move(cipher))) {
25
   // Not really true but only 128 bit allowed at the moment
26
0
   if(m_bs != 16) {
27
0
      throw Invalid_Argument("SIV requires a 128 bit block cipher");
28
0
   }
29
0
}
30
31
0
SIV_Mode::~SIV_Mode() = default;
32
33
0
void SIV_Mode::clear() {
34
0
   m_ctr->clear();
35
0
   m_mac->clear();
36
0
   reset();
37
0
}
38
39
0
void SIV_Mode::reset() {
40
0
   m_nonce.clear();
41
0
   m_msg_buf.clear();
42
0
   m_ad_macs.clear();
43
0
}
44
45
0
std::string SIV_Mode::name() const {
46
0
   return m_name;
47
0
}
48
49
0
bool SIV_Mode::valid_nonce_length(size_t /*nonce_len*/) const {
50
0
   return true;
51
0
}
52
53
0
size_t SIV_Mode::update_granularity() const {
54
0
   return 1;
55
0
}
56
57
0
size_t SIV_Mode::ideal_granularity() const {
58
   // Completely arbitrary value:
59
0
   return 128;
60
0
}
61
62
0
bool SIV_Mode::requires_entire_message() const {
63
0
   return true;
64
0
}
65
66
0
Key_Length_Specification SIV_Mode::key_spec() const {
67
0
   return m_mac->key_spec().multiple(2);
68
0
}
69
70
0
bool SIV_Mode::has_keying_material() const {
71
0
   return m_ctr->has_keying_material() && m_mac->has_keying_material();
72
0
}
73
74
0
void SIV_Mode::key_schedule(std::span<const uint8_t> key) {
75
0
   const size_t keylen = key.size() / 2;
76
0
   m_mac->set_key(key.first(keylen));
77
0
   m_ctr->set_key(key.last(keylen));
78
0
   m_ad_macs.clear();
79
0
}
80
81
0
size_t SIV_Mode::maximum_associated_data_inputs() const {
82
0
   return block_size() * 8 - 2;
83
0
}
84
85
0
void SIV_Mode::set_associated_data_n(size_t n, std::span<const uint8_t> ad) {
86
0
   const size_t max_ads = maximum_associated_data_inputs();
87
0
   if(n > max_ads) {
88
0
      throw Invalid_Argument(name() + " allows no more than " + std::to_string(max_ads) + " ADs");
89
0
   }
90
91
0
   if(n >= m_ad_macs.size()) {
92
0
      m_ad_macs.resize(n + 1);
93
0
   }
94
95
0
   m_ad_macs[n] = m_mac->process(ad);
96
0
}
97
98
0
void SIV_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
99
0
   if(!valid_nonce_length(nonce_len)) {
100
0
      throw Invalid_IV_Length(name(), nonce_len);
101
0
   }
102
103
0
   if(nonce_len) {
104
0
      m_nonce = m_mac->process(nonce, nonce_len);
105
0
   } else {
106
0
      m_nonce.clear();
107
0
   }
108
109
0
   m_msg_buf.clear();
110
0
}
111
112
0
size_t SIV_Mode::process_msg(uint8_t buf[], size_t sz) {
113
   // all output is saved for processing in finish
114
0
   m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
115
0
   return 0;
116
0
}
117
118
0
secure_vector<uint8_t> SIV_Mode::S2V(const uint8_t* text, size_t text_len) {
119
0
   const std::vector<uint8_t> zeros(block_size());
120
121
0
   secure_vector<uint8_t> V = m_mac->process(zeros.data(), zeros.size());
122
123
0
   for(size_t i = 0; i != m_ad_macs.size(); ++i) {
124
0
      poly_double_n(V.data(), V.size());
125
0
      V ^= m_ad_macs[i];
126
0
   }
127
128
0
   if(!m_nonce.empty()) {
129
0
      poly_double_n(V.data(), V.size());
130
0
      V ^= m_nonce;
131
0
   }
132
133
0
   if(text_len < block_size()) {
134
0
      poly_double_n(V.data(), V.size());
135
0
      xor_buf(V.data(), text, text_len);
136
0
      V[text_len] ^= 0x80;
137
0
      return m_mac->process(V);
138
0
   }
139
140
0
   m_mac->update(text, text_len - block_size());
141
0
   xor_buf(V.data(), &text[text_len - block_size()], block_size());
142
0
   m_mac->update(V);
143
144
0
   return m_mac->final();
145
0
}
146
147
0
void SIV_Mode::set_ctr_iv(secure_vector<uint8_t> V) {
148
0
   V[m_bs - 8] &= 0x7F;
149
0
   V[m_bs - 4] &= 0x7F;
150
151
0
   ctr().set_iv(V.data(), V.size());
152
0
}
153
154
0
void SIV_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
155
0
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
156
157
0
   buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
158
0
   msg_buf().clear();
159
160
0
   const secure_vector<uint8_t> V = S2V(buffer.data() + offset, buffer.size() - offset);
161
162
0
   buffer.insert(buffer.begin() + offset, V.begin(), V.end());
163
164
0
   if(buffer.size() != offset + V.size()) {
165
0
      set_ctr_iv(V);
166
0
      ctr().cipher1(&buffer[offset + V.size()], buffer.size() - offset - V.size());
167
0
   }
168
0
}
169
170
0
void SIV_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
171
0
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
172
173
0
   if(!msg_buf().empty()) {
174
0
      buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
175
0
      msg_buf().clear();
176
0
   }
177
178
0
   const size_t sz = buffer.size() - offset;
179
180
0
   BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
181
182
0
   secure_vector<uint8_t> V(buffer.data() + offset, buffer.data() + offset + block_size());
183
184
0
   if(buffer.size() != offset + V.size()) {
185
0
      set_ctr_iv(V);
186
187
0
      ctr().cipher(buffer.data() + offset + V.size(), buffer.data() + offset, buffer.size() - offset - V.size());
188
0
   }
189
190
0
   const secure_vector<uint8_t> T = S2V(buffer.data() + offset, buffer.size() - offset - V.size());
191
192
0
   if(!CT::is_equal(T.data(), V.data(), T.size()).as_bool()) {
193
0
      throw Invalid_Authentication_Tag("SIV tag check failed");
194
0
   }
195
196
0
   buffer.resize(buffer.size() - tag_size());
197
0
}
198
199
}  // namespace Botan