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

randombit / botan / 23487963139

24 Mar 2026 11:43AM UTC coverage: 89.456% (+0.007%) from 89.449%
23487963139

push

github

web-flow
Merge pull request #5480 from randombit/jack/ctr-simd32

Add SIMD_4x32 based counter increment and XOR for CTR

105117 of 117507 relevant lines covered (89.46%)

11792114.42 hits per line

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

93.22
/src/lib/stream/ctr/ctr.cpp
1
/*
2
* Counter mode
3
* (C) 1999-2011,2014 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include <botan/internal/ctr.h>
9

10
#include <botan/exceptn.h>
11
#include <botan/internal/bit_ops.h>
12
#include <botan/internal/fmt.h>
13
#include <botan/internal/loadstor.h>
14

15
#if defined(BOTAN_HAS_CTR_BE_AVX2) || defined(BOTAN_HAS_CTR_BE_SIMD32)
16
   #include <botan/internal/cpuid.h>
17
#endif
18

19
namespace Botan {
20

21
CTR_BE::CTR_BE(std::unique_ptr<BlockCipher> cipher) :
1,023✔
22
      m_cipher(std::move(cipher)),
1,023✔
23
      m_block_size(m_cipher->block_size()),
1,023✔
24
      m_ctr_size(m_block_size),
1,023✔
25
      m_ctr_blocks(m_cipher->parallel_bytes() / m_block_size),
1,023✔
26
      m_counter(m_cipher->parallel_bytes()),
2,046✔
27
      m_pad(m_counter.size()),
1,023✔
28
      m_pad_pos(0) {}
2,046✔
29

30
CTR_BE::CTR_BE(std::unique_ptr<BlockCipher> cipher, size_t ctr_size) :
100,619✔
31
      m_cipher(std::move(cipher)),
100,619✔
32
      m_block_size(m_cipher->block_size()),
100,619✔
33
      m_ctr_size(ctr_size),
100,619✔
34
      m_ctr_blocks(m_cipher->parallel_bytes() / m_block_size),
100,619✔
35
      m_counter(m_cipher->parallel_bytes()),
201,238✔
36
      m_pad(m_counter.size()),
100,619✔
37
      m_pad_pos(0) {
201,238✔
38
   BOTAN_ARG_CHECK(m_ctr_size >= 4 && m_ctr_size <= m_block_size, "Invalid CTR-BE counter size");
100,619✔
39
}
100,619✔
40

41
void CTR_BE::clear() {
7,504✔
42
   m_cipher->clear();
7,504✔
43
   zeroise(m_pad);
7,504✔
44
   zeroise(m_counter);
7,504✔
45
   zap(m_iv);
7,504✔
46
   m_pad_pos = 0;
7,504✔
47
}
7,504✔
48

49
size_t CTR_BE::default_iv_length() const {
3,280✔
50
   return m_block_size;
3,280✔
51
}
52

53
bool CTR_BE::valid_iv_length(size_t iv_len) const {
326,989✔
54
   return (iv_len <= m_block_size);
326,989✔
55
}
56

57
size_t CTR_BE::buffer_size() const {
1,661✔
58
   return m_pad.size();
1,661✔
59
}
60

61
Key_Length_Specification CTR_BE::key_spec() const {
207,711✔
62
   return m_cipher->key_spec();
207,711✔
63
}
64

65
std::unique_ptr<StreamCipher> CTR_BE::new_object() const {
1,640✔
66
   return std::make_unique<CTR_BE>(m_cipher->new_object(), m_ctr_size);
1,640✔
67
}
68

69
bool CTR_BE::has_keying_material() const {
20,978,281✔
70
   return m_cipher->has_keying_material();
20,978,281✔
71
}
72

73
void CTR_BE::key_schedule(std::span<const uint8_t> key) {
104,675✔
74
   m_cipher->set_key(key);
104,675✔
75

76
   // Set a default all-zeros IV
77
   set_iv(nullptr, 0);
104,675✔
78
}
104,675✔
79

80
std::string CTR_BE::name() const {
14,119✔
81
   if(m_ctr_size == m_block_size) {
14,119✔
82
      return fmt("CTR-BE({})", m_cipher->name());
13,065✔
83
   } else {
84
      return fmt("CTR-BE({},{})", m_cipher->name(), m_ctr_size);
1,054✔
85
   }
86
}
87

88
void CTR_BE::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) {
131,465✔
89
   assert_key_material_set();
131,465✔
90

91
   const uint8_t* pad_bits = m_pad.data();
127,509✔
92
   const size_t pad_size = m_pad.size();
127,509✔
93

94
   /* Consume any already computed keystream in m_pad */
95

96
   if(m_pad_pos > 0) {
127,509✔
97
      const size_t avail = pad_size - m_pad_pos;
106,300✔
98
      const size_t take = std::min(length, avail);
106,300✔
99
      xor_buf(out, in, pad_bits + m_pad_pos, take);
106,300✔
100
      length -= take;
106,300✔
101
      in += take;
106,300✔
102
      out += take;
106,300✔
103
      m_pad_pos += take;
106,300✔
104

105
      if(take == avail) {
106,300✔
106
         add_counter(m_ctr_blocks);
2,474✔
107
         m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
2,474✔
108
         m_pad_pos = 0;
2,474✔
109
      }
110
   }
111

112
   /* Bulk processing */
113

114
   [[maybe_unused]] const bool can_use_bs16_ctr4_fastpath = m_block_size == 16 && m_ctr_size == 4 && pad_size % 64 == 0;
127,509✔
115

116
#if defined(BOTAN_HAS_CTR_BE_AVX2)
117
   if(length >= pad_size && can_use_bs16_ctr4_fastpath && CPUID::has(CPUID::Feature::AVX2)) {
127,509✔
118
      const size_t consumed = ctr_proc_bs16_ctr4_avx2(in, out, length);
1,443✔
119
      in += consumed;
1,443✔
120
      out += consumed;
1,443✔
121
      length -= consumed;
1,443✔
122
   }
123
#endif
124

125
#if defined(BOTAN_HAS_CTR_BE_SIMD32)
126
   if(length >= pad_size && can_use_bs16_ctr4_fastpath && CPUID::has(CPUID::Feature::SIMD_4X32)) {
127,509✔
127
      const size_t consumed = ctr_proc_bs16_ctr4_simd32(in, out, length);
6✔
128
      in += consumed;
6✔
129
      out += consumed;
6✔
130
      length -= consumed;
6✔
131
   }
132
#endif
133

134
   while(length >= pad_size) {
127,664✔
135
      xor_buf(out, in, pad_bits, pad_size);
155✔
136
      length -= pad_size;
155✔
137
      in += pad_size;
155✔
138
      out += pad_size;
155✔
139

140
      add_counter(m_ctr_blocks);
155✔
141
      m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
155✔
142
   }
143

144
   /* Now if length > 0 then we have some remaining text, and m_pad is full - consume as required */
145
   if(length > 0) {
127,509✔
146
      xor_buf(out, in, pad_bits, length);
23,040✔
147
      m_pad_pos = length;
23,040✔
148
   }
149
}
127,509✔
150

151
void CTR_BE::generate_keystream(uint8_t out[], size_t length) {
20,604,187✔
152
   assert_key_material_set();
20,604,187✔
153

154
   const size_t avail = m_pad.size() - m_pad_pos;
20,604,187✔
155
   const size_t take = std::min(length, avail);
20,604,187✔
156
   copy_mem(out, &m_pad[m_pad_pos], take);
20,604,187✔
157
   length -= take;
20,604,187✔
158
   out += take;
20,604,187✔
159
   m_pad_pos += take;
20,604,187✔
160

161
   while(length >= m_pad.size()) {
20,604,189✔
162
      add_counter(m_ctr_blocks);
2✔
163
      m_cipher->encrypt_n(m_counter.data(), out, m_ctr_blocks);
2✔
164

165
      length -= m_pad.size();
2✔
166
      out += m_pad.size();
2✔
167
   }
168

169
   if(m_pad_pos == m_pad.size()) {
20,604,187✔
170
      add_counter(m_ctr_blocks);
84,897✔
171
      m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
84,897✔
172
      m_pad_pos = 0;
84,897✔
173
   }
174

175
   copy_mem(out, m_pad.data(), length);
20,604,187✔
176
   m_pad_pos += length;
20,604,187✔
177
   BOTAN_ASSERT_NOMSG(m_pad_pos < m_pad.size());
20,604,187✔
178
}
20,604,187✔
179

180
void CTR_BE::set_iv_bytes(const uint8_t iv[], size_t iv_len) {
227,730✔
181
   if(!valid_iv_length(iv_len)) {
227,730✔
182
      throw Invalid_IV_Length(name(), iv_len);
3,288✔
183
   }
184

185
   m_iv.resize(m_block_size);
226,086✔
186
   zeroise(m_iv);
226,086✔
187
   copy_mem(m_iv.data(), iv, iv_len);
226,086✔
188

189
   seek(0);
226,086✔
190
}
224,132✔
191

192
void CTR_BE::add_counter(const uint64_t counter) {
87,552✔
193
   const size_t ctr_size = m_ctr_size;
87,552✔
194
   const size_t ctr_blocks = m_ctr_blocks;
87,552✔
195
   const size_t BS = m_block_size;
87,552✔
196

197
   if(ctr_size == 4) {
87,552✔
198
      const size_t off = (BS - 4);
2,460✔
199
      const uint32_t low32 = static_cast<uint32_t>(counter + load_be<uint32_t>(&m_counter[off], 0));
2,460✔
200

201
      for(size_t i = 0; i != ctr_blocks; ++i) {
61,220✔
202
         store_be(uint32_t(low32 + i), &m_counter[i * BS + off]);
58,760✔
203
      }
204
   } else if(ctr_size == 8) {
85,092✔
205
      const size_t off = (BS - 8);
38✔
206
      const uint64_t low64 = counter + load_be<uint64_t>(&m_counter[off], 0);
38✔
207

208
      for(size_t i = 0; i != ctr_blocks; ++i) {
1,574✔
209
         store_be(uint64_t(low64 + i), &m_counter[i * BS + off]);
1,536✔
210
      }
211
   } else if(ctr_size == 16) {
85,054✔
212
      const size_t off = (BS - 16);
85,054✔
213
      uint64_t b0 = load_be<uint64_t>(&m_counter[off], 0);
85,054✔
214
      uint64_t b1 = load_be<uint64_t>(&m_counter[off], 1);
85,054✔
215
      b1 += counter;
85,054✔
216
      b0 += (b1 < counter) ? 1 : 0;  // carry
85,054✔
217

218
      for(size_t i = 0; i != ctr_blocks; ++i) {
2,749,158✔
219
         store_be(b0, &m_counter[i * BS + off]);
2,664,104✔
220
         store_be(b1, &m_counter[i * BS + off + 8]);
2,664,104✔
221
         b1 += 1;
2,664,104✔
222
         if(b1 == 0) {
2,664,104✔
223
            b0 += 1;  // carry
×
224
         }
225
      }
226
   } else {
227
      for(size_t i = 0; i != ctr_blocks; ++i) {
×
228
         uint64_t local_counter = counter;
×
229
         uint16_t carry = static_cast<uint8_t>(local_counter);
×
230
         for(size_t j = 0; (carry > 0 || local_counter > 0) && j != ctr_size; ++j) {
×
231
            const size_t off = i * BS + (BS - 1 - j);
×
232
            const uint16_t cnt = static_cast<uint16_t>(m_counter[off]) + carry;
×
233
            m_counter[off] = static_cast<uint8_t>(cnt);
×
234
            local_counter = (local_counter >> 8);
×
235
            carry = (cnt >> 8) + static_cast<uint8_t>(local_counter);
×
236
         }
237
      }
238
   }
239
}
87,552✔
240

241
void CTR_BE::seek(uint64_t offset) {
230,926✔
242
   assert_key_material_set();
230,926✔
243

244
   const uint64_t base_counter = m_ctr_blocks * (offset / m_counter.size());
227,332✔
245

246
   zeroise(m_counter);
227,332✔
247
   BOTAN_ASSERT_NOMSG(m_counter.size() >= m_iv.size());
227,332✔
248
   copy_mem(m_counter.data(), m_iv.data(), m_iv.size());
227,332✔
249

250
   const size_t BS = m_block_size;
227,332✔
251

252
   // Set m_counter blocks to IV, IV + 1, ... IV + n
253

254
   if(m_ctr_size == 4 && BS >= 8) {
227,332✔
255
      const uint32_t low32 = load_be<uint32_t>(&m_counter[BS - 4], 0);
17,418✔
256

257
      if(m_ctr_blocks >= 4 && is_power_of_2(m_ctr_blocks)) {
17,418✔
258
         size_t written = 1;
259
         while(written < m_ctr_blocks) {
102,662✔
260
            copy_mem(&m_counter[written * BS], &m_counter[0], BS * written);  // NOLINT(*container-data-pointer)
85,244✔
261
            written *= 2;
85,244✔
262
         }
263
      } else {
264
         for(size_t i = 1; i != m_ctr_blocks; ++i) {
×
265
            copy_mem(&m_counter[i * BS], &m_counter[0], BS - 4);  // NOLINT(*container-data-pointer)
×
266
         }
267
      }
268

269
      for(size_t i = 1; i != m_ctr_blocks; ++i) {
538,200✔
270
         const uint32_t c = static_cast<uint32_t>(low32 + i);
520,782✔
271
         store_be(c, &m_counter[(BS - 4) + i * BS]);
520,782✔
272
      }
273
   } else {
274
      // do everything sequentially:
275
      for(size_t i = 1; i != m_ctr_blocks; ++i) {
7,061,012✔
276
         copy_mem(&m_counter[i * BS], &m_counter[(i - 1) * BS], BS);
6,851,098✔
277

278
         for(size_t j = 0; j != m_ctr_size; ++j) {
6,857,269✔
279
            uint8_t& c = m_counter[i * BS + (BS - 1 - j)];
6,857,212✔
280
            c += 1;
6,857,212✔
281
            if(c > 0) {
6,857,212✔
282
               break;
283
            }
284
         }
285
      }
286
   }
287

288
   if(base_counter > 0) {
227,332✔
289
      add_counter(base_counter);
24✔
290
   }
291

292
   m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
227,332✔
293
   m_pad_pos = offset % m_counter.size();
227,332✔
294
}
227,332✔
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