• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 16239649803

12 Jul 2025 03:54PM UTC coverage: 90.588% (+0.02%) from 90.572%
16239649803

Pull #4880

github

web-flow
Merge 79ae69b5e into e3b3452ec
Pull Request #4880: Refactor: Cipher_Mode::finish_msg() using std::span

99178 of 109483 relevant lines covered (90.59%)

12365337.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.93
/src/lib/modes/cbc/cbc.cpp
1
/*
2
* CBC Mode
3
* (C) 1999-2007,2013,2017 Jack Lloyd
4
* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
5
* (C) 2018 Ribose Inc
6
*
7
* Botan is released under the Simplified BSD License (see license.txt)
8
*/
9

10
#include <botan/internal/cbc.h>
11

12
#include <botan/mem_ops.h>
13
#include <botan/internal/fmt.h>
14
#include <botan/internal/mode_pad.h>
15
#include <botan/internal/rounding.h>
16

17
namespace Botan {
18

19
namespace {
20

21
constexpr void swap_bytes(std::span<uint8_t> a, std::span<uint8_t> b) {
64✔
22
   BOTAN_DEBUG_ASSERT(a.size() == b.size());
64✔
23
   for(size_t i = 0; i != a.size(); ++i) {
2,307✔
24
      std::swap(a[i], b[i]);
2,078✔
25
   }
26
}
27

28
}  // namespace
29

30
CBC_Mode::CBC_Mode(std::unique_ptr<BlockCipher> cipher, std::unique_ptr<BlockCipherModePaddingMethod> padding) :
3,467✔
31
      m_cipher(std::move(cipher)), m_padding(std::move(padding)), m_block_size(m_cipher->block_size()) {
3,467✔
32
   if(m_padding && !m_padding->valid_blocksize(m_block_size)) {
3,467✔
33
      throw Invalid_Argument(fmt("Padding {} cannot be used with {} in CBC mode", m_padding->name(), m_cipher->name()));
×
34
   }
35
}
3,467✔
36

37
void CBC_Mode::clear() {
660✔
38
   m_cipher->clear();
660✔
39
   reset();
660✔
40
}
660✔
41

42
void CBC_Mode::reset() {
1,976✔
43
   m_state.clear();
989✔
44
}
989✔
45

46
std::string CBC_Mode::name() const {
1,318✔
47
   if(m_padding) {
1,318✔
48
      return fmt("{}/CBC/{}", cipher().name(), padding().name());
1,126✔
49
   } else {
50
      return fmt("{}/CBC/CTS", cipher().name());
384✔
51
   }
52
}
53

54
size_t CBC_Mode::update_granularity() const {
2,308✔
55
   return cipher().block_size();
2,308✔
56
}
57

58
size_t CBC_Mode::ideal_granularity() const {
3,547✔
59
   return cipher().parallel_bytes();
3,547✔
60
}
61

62
Key_Length_Specification CBC_Mode::key_spec() const {
6,106✔
63
   return cipher().key_spec();
6,106✔
64
}
65

66
size_t CBC_Mode::default_nonce_length() const {
1,179✔
67
   return block_size();
1,179✔
68
}
69

70
bool CBC_Mode::valid_nonce_length(size_t n) const {
8,760✔
71
   return (n == 0 || n == block_size());
8,760✔
72
}
73

74
bool CBC_Mode::has_keying_material() const {
1,974✔
75
   return m_cipher->has_keying_material();
1,974✔
76
}
77

78
void CBC_Mode::key_schedule(std::span<const uint8_t> key) {
4,020✔
79
   m_cipher->set_key(key);
4,020✔
80
   m_state.clear();
4,020✔
81
}
4,020✔
82

83
void CBC_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
8,310✔
84
   if(!valid_nonce_length(nonce_len)) {
8,310✔
85
      throw Invalid_IV_Length(name(), nonce_len);
1,316✔
86
   }
87

88
   /*
89
   * A nonce of zero length means carry the last ciphertext value over
90
   * as the new IV, as unfortunately some protocols require this. If
91
   * this is the first message then we use an IV of all zeros.
92
   */
93
   if(nonce_len > 0) {
7,652✔
94
      m_state.assign(nonce, nonce + nonce_len);
7,632✔
95
   } else if(m_state.empty()) {
20✔
96
      m_state.resize(m_cipher->block_size());
14✔
97
   }
98
   // else leave the state alone
99
}
7,652✔
100

101
size_t CBC_Encryption::minimum_final_size() const {
2,959✔
102
   return 0;
2,959✔
103
}
104

105
size_t CBC_Encryption::output_length(size_t input_length) const {
2,958✔
106
   return padding().output_length(input_length, block_size());
2,958✔
107
}
108

109
size_t CBC_Encryption::process_msg(uint8_t buf[], size_t sz) {
5,700✔
110
   BOTAN_STATE_CHECK(!state().empty());
5,700✔
111
   const size_t BS = block_size();
4,713✔
112

113
   BOTAN_ARG_CHECK(sz % BS == 0, "CBC input is not full blocks");
4,713✔
114
   const size_t blocks = sz / BS;
4,713✔
115

116
   if(blocks > 0) {
4,713✔
117
      xor_buf(&buf[0], state_ptr(), BS);
4,557✔
118
      cipher().encrypt(&buf[0]);
4,557✔
119

120
      for(size_t i = 1; i != blocks; ++i) {
206,140✔
121
         xor_buf(&buf[BS * i], &buf[BS * (i - 1)], BS);
201,583✔
122
         cipher().encrypt(&buf[BS * i]);
201,583✔
123
      }
124

125
      state().assign(&buf[BS * (blocks - 1)], &buf[BS * blocks]);
4,557✔
126
   }
127

128
   return sz;
4,713✔
129
}
130

131
size_t CBC_Encryption::finish_msg(std::span<uint8_t> buffer, size_t input_bytes) {
2,675✔
132
   BOTAN_STATE_CHECK(!state().empty());
2,675✔
133
   BOTAN_DEBUG_ASSERT(buffer.size() == bytes_needed_for_finalization(input_bytes));
1,832✔
134

135
   const size_t BS = block_size();
1,832✔
136

137
   const size_t bytes_in_final_block = input_bytes % BS;
1,832✔
138
   padding().add_padding(buffer, bytes_in_final_block, BS);
1,832✔
139
   process(buffer);
1,832✔
140

141
   return buffer.size();
1,832✔
142
}
143

144
bool CTS_Encryption::valid_nonce_length(size_t n) const {
437✔
145
   return (n == block_size());
437✔
146
}
147

148
size_t CTS_Encryption::minimum_final_size() const {
389✔
149
   return block_size() + 1;
389✔
150
}
151

152
size_t CTS_Encryption::output_length(size_t input_length) const {
389✔
153
   return input_length;  // no ciphertext expansion in CTS
389✔
154
}
155

156
size_t CTS_Encryption::finish_msg(std::span<uint8_t> buffer, size_t input_bytes) {
341✔
157
   BOTAN_STATE_CHECK(!state().empty());
341✔
158
   BOTAN_ASSERT_NOMSG(buffer.size() == input_bytes);
197✔
159

160
   const size_t BS = block_size();
197✔
161

162
   if(input_bytes < BS + 1) {
197✔
163
      throw Encoding_Error(Botan::fmt("{}: insufficient data to encrypt", name()));
×
164
   }
165

166
   if(input_bytes % BS == 0) {
197✔
167
      process(buffer);
32✔
168
      const auto last_block = buffer.last(BS);
32✔
169
      const auto second_last_block = buffer.last(BS * 2).first(BS);
32✔
170
      swap_bytes(last_block, second_last_block);
64✔
171
   } else {
172
      const size_t full_blocks = ((buffer.size() / BS) - 1) * BS;
165✔
173
      const auto tail = buffer.subspan(full_blocks);
165✔
174
      BOTAN_ASSERT(tail.size() > BS && tail.size() < 2 * BS, "Left over size in expected range");
165✔
175

176
      const auto final_ordinary_blocks = buffer.first(full_blocks);
165✔
177
      const auto first_block_of_tail = tail.first(BS);
165✔
178
      const auto final_bytes_of_tail = tail.subspan(BS);
165✔
179
      const auto first_bytes_of_tail = tail.first(final_bytes_of_tail.size());
165✔
180

181
      process(final_ordinary_blocks);
165✔
182
      xor_buf(first_block_of_tail, state());
165✔
183
      cipher().encrypt(first_block_of_tail);
1,025✔
184
      swap_bytes(first_bytes_of_tail, final_bytes_of_tail);
165✔
185
      xor_buf(first_bytes_of_tail, final_bytes_of_tail);
165✔
186
      cipher().encrypt(first_block_of_tail);
330✔
187
   }
188

189
   return buffer.size();
197✔
190
}
191

192
size_t CBC_Decryption::output_length(size_t input_length) const {
3,295✔
193
   return input_length;  // precise for CTS, worst case otherwise
3,295✔
194
}
195

196
size_t CBC_Decryption::minimum_final_size() const {
2,908✔
197
   return block_size();
2,908✔
198
}
199

200
size_t CBC_Decryption::process_msg(uint8_t buf[], size_t sz) {
5,880✔
201
   BOTAN_STATE_CHECK(!state().empty());
5,880✔
202

203
   const size_t BS = block_size();
4,893✔
204

205
   BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
4,893✔
206
   size_t blocks = sz / BS;
4,893✔
207

208
   while(blocks > 0) {
17,120✔
209
      const size_t to_proc = std::min(BS * blocks, m_tempbuf.size());
12,227✔
210

211
      cipher().decrypt_n(buf, m_tempbuf.data(), to_proc / BS);
12,227✔
212

213
      xor_buf(m_tempbuf.data(), state_ptr(), BS);
12,227✔
214
      xor_buf(&m_tempbuf[BS], buf, to_proc - BS);
12,227✔
215
      copy_mem(state_ptr(), buf + (to_proc - BS), BS);
12,227✔
216

217
      copy_mem(buf, m_tempbuf.data(), to_proc);
12,227✔
218

219
      buf += to_proc;
12,227✔
220
      blocks -= to_proc / BS;
12,227✔
221
   }
222

223
   return sz;
4,893✔
224
}
225

226
size_t CBC_Decryption::finish_msg(std::span<uint8_t> buffer, size_t input_bytes) {
2,623✔
227
   BOTAN_STATE_CHECK(!state().empty());
2,623✔
228
   BOTAN_ASSERT_NOMSG(buffer.size() == input_bytes);
1,780✔
229

230
   const size_t BS = block_size();
1,780✔
231

232
   if(buffer.empty() || buffer.size() % BS != 0) {
1,780✔
233
      throw Decoding_Error(Botan::fmt("{}: Ciphertext not a multiple of block size", name()));
×
234
   }
235

236
   process(buffer);
1,780✔
237

238
   const size_t pad_bytes = BS - padding().unpad(std::span{buffer}.last(BS));
1,780✔
239
   if(pad_bytes == 0 && padding().name() != "NoPadding") {
1,866✔
240
      throw Decoding_Error("Invalid CBC padding");
×
241
   }
242

243
   return buffer.size() - pad_bytes;
1,780✔
244
}
245

246
void CBC_Decryption::reset() {
987✔
247
   CBC_Mode::reset();
987✔
248
   zeroise(m_tempbuf);
987✔
249
}
987✔
250

251
bool CTS_Decryption::valid_nonce_length(size_t n) const {
437✔
252
   return (n == block_size());
437✔
253
}
254

255
size_t CTS_Decryption::minimum_final_size() const {
389✔
256
   return block_size() + 1;
389✔
257
}
258

259
size_t CTS_Decryption::finish_msg(std::span<uint8_t> buffer, size_t input_bytes) {
341✔
260
   BOTAN_STATE_CHECK(!state().empty());
341✔
261
   BOTAN_ASSERT_NOMSG(buffer.size() == input_bytes);
197✔
262

263
   const size_t BS = block_size();
197✔
264

265
   if(buffer.size() < BS + 1) {
197✔
266
      throw Encoding_Error(Botan::fmt("{}: insufficient data to decrypt", name()));
×
267
   }
268

269
   if(buffer.size() % BS == 0) {
197✔
270
      const auto last_block = buffer.last(BS);
32✔
271
      const auto second_last_block = buffer.last(BS * 2).first(BS);
32✔
272
      swap_bytes(last_block, second_last_block);
32✔
273
      process(buffer);
32✔
274
   } else {
275
      const size_t full_blocks = ((buffer.size() / BS) - 1) * BS;
165✔
276
      const auto tail = buffer.subspan(full_blocks);
165✔
277
      BOTAN_ASSERT(tail.size() > BS && tail.size() < 2 * BS, "Left over size in expected range");
165✔
278

279
      const auto final_ordinary_blocks = buffer.first(full_blocks);
165✔
280
      const auto first_block_of_tail = tail.first(BS);
165✔
281
      const auto final_bytes_of_tail = tail.subspan(BS);
165✔
282
      const auto first_bytes_of_tail = tail.first(final_bytes_of_tail.size());
165✔
283

284
      process(final_ordinary_blocks);
165✔
285
      cipher().decrypt(first_block_of_tail);
165✔
286
      xor_buf(first_bytes_of_tail, final_bytes_of_tail);
165✔
287
      swap_bytes(first_bytes_of_tail, final_bytes_of_tail);
165✔
288
      cipher().decrypt(first_block_of_tail);
165✔
289
      xor_buf(first_block_of_tail, state());
165✔
290
   }
291

292
   return buffer.size();
197✔
293
}
294

295
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc