/src/botan/src/lib/stream/ctr/ctr.cpp
Line  | Count  | Source (jump to first uncovered line)  | 
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  |  | namespace Botan { | 
16  |  |  | 
17  |  | CTR_BE::CTR_BE(std::unique_ptr<BlockCipher> cipher) :  | 
18  |  |       m_cipher(std::move(cipher)),  | 
19  |  |       m_block_size(m_cipher->block_size()),  | 
20  |  |       m_ctr_size(m_block_size),  | 
21  |  |       m_ctr_blocks(m_cipher->parallel_bytes() / m_block_size),  | 
22  |  |       m_counter(m_cipher->parallel_bytes()),  | 
23  |  |       m_pad(m_counter.size()),  | 
24  | 0  |       m_pad_pos(0) {} | 
25  |  |  | 
26  |  | CTR_BE::CTR_BE(std::unique_ptr<BlockCipher> cipher, size_t ctr_size) :  | 
27  |  |       m_cipher(std::move(cipher)),  | 
28  |  |       m_block_size(m_cipher->block_size()),  | 
29  |  |       m_ctr_size(ctr_size),  | 
30  |  |       m_ctr_blocks(m_cipher->parallel_bytes() / m_block_size),  | 
31  |  |       m_counter(m_cipher->parallel_bytes()),  | 
32  |  |       m_pad(m_counter.size()),  | 
33  | 0  |       m_pad_pos(0) { | 
34  | 0  |    BOTAN_ARG_CHECK(m_ctr_size >= 4 && m_ctr_size <= m_block_size, "Invalid CTR-BE counter size");  | 
35  | 0  | }  | 
36  |  |  | 
37  | 0  | void CTR_BE::clear() { | 
38  | 0  |    m_cipher->clear();  | 
39  | 0  |    zeroise(m_pad);  | 
40  | 0  |    zeroise(m_counter);  | 
41  | 0  |    zap(m_iv);  | 
42  | 0  |    m_pad_pos = 0;  | 
43  | 0  | }  | 
44  |  |  | 
45  | 0  | size_t CTR_BE::default_iv_length() const { | 
46  | 0  |    return m_block_size;  | 
47  | 0  | }  | 
48  |  |  | 
49  | 0  | bool CTR_BE::valid_iv_length(size_t iv_len) const { | 
50  | 0  |    return (iv_len <= m_block_size);  | 
51  | 0  | }  | 
52  |  |  | 
53  | 0  | size_t CTR_BE::buffer_size() const { | 
54  | 0  |    return m_pad.size();  | 
55  | 0  | }  | 
56  |  |  | 
57  | 0  | Key_Length_Specification CTR_BE::key_spec() const { | 
58  | 0  |    return m_cipher->key_spec();  | 
59  | 0  | }  | 
60  |  |  | 
61  | 0  | std::unique_ptr<StreamCipher> CTR_BE::new_object() const { | 
62  | 0  |    return std::make_unique<CTR_BE>(m_cipher->new_object(), m_ctr_size);  | 
63  | 0  | }  | 
64  |  |  | 
65  | 0  | bool CTR_BE::has_keying_material() const { | 
66  | 0  |    return m_cipher->has_keying_material();  | 
67  | 0  | }  | 
68  |  |  | 
69  | 0  | void CTR_BE::key_schedule(std::span<const uint8_t> key) { | 
70  | 0  |    m_cipher->set_key(key);  | 
71  |  |  | 
72  |  |    // Set a default all-zeros IV  | 
73  | 0  |    set_iv(nullptr, 0);  | 
74  | 0  | }  | 
75  |  |  | 
76  | 0  | std::string CTR_BE::name() const { | 
77  | 0  |    if(m_ctr_size == m_block_size) { | 
78  | 0  |       return fmt("CTR-BE({})", m_cipher->name()); | 
79  | 0  |    } else { | 
80  | 0  |       return fmt("CTR-BE({},{})", m_cipher->name(), m_ctr_size); | 
81  | 0  |    }  | 
82  | 0  | }  | 
83  |  |  | 
84  | 0  | void CTR_BE::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) { | 
85  | 0  |    assert_key_material_set();  | 
86  |  | 
  | 
87  | 0  |    const uint8_t* pad_bits = &m_pad[0];  | 
88  | 0  |    const size_t pad_size = m_pad.size();  | 
89  |  | 
  | 
90  | 0  |    if(m_pad_pos > 0) { | 
91  | 0  |       const size_t avail = pad_size - m_pad_pos;  | 
92  | 0  |       const size_t take = std::min(length, avail);  | 
93  | 0  |       xor_buf(out, in, pad_bits + m_pad_pos, take);  | 
94  | 0  |       length -= take;  | 
95  | 0  |       in += take;  | 
96  | 0  |       out += take;  | 
97  | 0  |       m_pad_pos += take;  | 
98  |  | 
  | 
99  | 0  |       if(take == avail) { | 
100  | 0  |          add_counter(m_ctr_blocks);  | 
101  | 0  |          m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);  | 
102  | 0  |          m_pad_pos = 0;  | 
103  | 0  |       }  | 
104  | 0  |    }  | 
105  |  | 
  | 
106  | 0  |    while(length >= pad_size) { | 
107  | 0  |       xor_buf(out, in, pad_bits, pad_size);  | 
108  | 0  |       length -= pad_size;  | 
109  | 0  |       in += pad_size;  | 
110  | 0  |       out += pad_size;  | 
111  |  | 
  | 
112  | 0  |       add_counter(m_ctr_blocks);  | 
113  | 0  |       m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);  | 
114  | 0  |    }  | 
115  |  | 
  | 
116  | 0  |    xor_buf(out, in, pad_bits, length);  | 
117  | 0  |    m_pad_pos += length;  | 
118  | 0  | }  | 
119  |  |  | 
120  | 0  | void CTR_BE::generate_keystream(uint8_t out[], size_t length) { | 
121  | 0  |    assert_key_material_set();  | 
122  |  | 
  | 
123  | 0  |    const size_t avail = m_pad.size() - m_pad_pos;  | 
124  | 0  |    const size_t take = std::min(length, avail);  | 
125  | 0  |    copy_mem(out, &m_pad[m_pad_pos], take);  | 
126  | 0  |    length -= take;  | 
127  | 0  |    out += take;  | 
128  | 0  |    m_pad_pos += take;  | 
129  |  | 
  | 
130  | 0  |    while(length >= m_pad.size()) { | 
131  | 0  |       add_counter(m_ctr_blocks);  | 
132  | 0  |       m_cipher->encrypt_n(m_counter.data(), out, m_ctr_blocks);  | 
133  |  | 
  | 
134  | 0  |       length -= m_pad.size();  | 
135  | 0  |       out += m_pad.size();  | 
136  | 0  |    }  | 
137  |  | 
  | 
138  | 0  |    if(m_pad_pos == m_pad.size()) { | 
139  | 0  |       add_counter(m_ctr_blocks);  | 
140  | 0  |       m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);  | 
141  | 0  |       m_pad_pos = 0;  | 
142  | 0  |    }  | 
143  |  | 
  | 
144  | 0  |    copy_mem(out, &m_pad[0], length);  | 
145  | 0  |    m_pad_pos += length;  | 
146  | 0  |    BOTAN_ASSERT_NOMSG(m_pad_pos < m_pad.size());  | 
147  | 0  | }  | 
148  |  |  | 
149  | 0  | void CTR_BE::set_iv_bytes(const uint8_t iv[], size_t iv_len) { | 
150  | 0  |    if(!valid_iv_length(iv_len)) { | 
151  | 0  |       throw Invalid_IV_Length(name(), iv_len);  | 
152  | 0  |    }  | 
153  |  |  | 
154  | 0  |    m_iv.resize(m_block_size);  | 
155  | 0  |    zeroise(m_iv);  | 
156  | 0  |    copy_mem(&m_iv[0], iv, iv_len);  | 
157  |  | 
  | 
158  | 0  |    seek(0);  | 
159  | 0  | }  | 
160  |  |  | 
161  | 0  | void CTR_BE::add_counter(const uint64_t counter) { | 
162  | 0  |    const size_t ctr_size = m_ctr_size;  | 
163  | 0  |    const size_t ctr_blocks = m_ctr_blocks;  | 
164  | 0  |    const size_t BS = m_block_size;  | 
165  |  | 
  | 
166  | 0  |    if(ctr_size == 4) { | 
167  | 0  |       const size_t off = (BS - 4);  | 
168  | 0  |       const uint32_t low32 = static_cast<uint32_t>(counter + load_be<uint32_t>(&m_counter[off], 0));  | 
169  |  | 
  | 
170  | 0  |       for(size_t i = 0; i != ctr_blocks; ++i) { | 
171  | 0  |          store_be(uint32_t(low32 + i), &m_counter[i * BS + off]);  | 
172  | 0  |       }  | 
173  | 0  |    } else if(ctr_size == 8) { | 
174  | 0  |       const size_t off = (BS - 8);  | 
175  | 0  |       const uint64_t low64 = counter + load_be<uint64_t>(&m_counter[off], 0);  | 
176  |  | 
  | 
177  | 0  |       for(size_t i = 0; i != ctr_blocks; ++i) { | 
178  | 0  |          store_be(uint64_t(low64 + i), &m_counter[i * BS + off]);  | 
179  | 0  |       }  | 
180  | 0  |    } else if(ctr_size == 16) { | 
181  | 0  |       const size_t off = (BS - 16);  | 
182  | 0  |       uint64_t b0 = load_be<uint64_t>(&m_counter[off], 0);  | 
183  | 0  |       uint64_t b1 = load_be<uint64_t>(&m_counter[off], 1);  | 
184  | 0  |       b1 += counter;  | 
185  | 0  |       b0 += (b1 < counter) ? 1 : 0;  // carry  | 
186  |  | 
  | 
187  | 0  |       for(size_t i = 0; i != ctr_blocks; ++i) { | 
188  | 0  |          store_be(b0, &m_counter[i * BS + off]);  | 
189  | 0  |          store_be(b1, &m_counter[i * BS + off + 8]);  | 
190  | 0  |          b1 += 1;  | 
191  | 0  |          b0 += (b1 == 0);  // carry  | 
192  | 0  |       }  | 
193  | 0  |    } else { | 
194  | 0  |       for(size_t i = 0; i != ctr_blocks; ++i) { | 
195  | 0  |          uint64_t local_counter = counter;  | 
196  | 0  |          uint16_t carry = static_cast<uint8_t>(local_counter);  | 
197  | 0  |          for(size_t j = 0; (carry || local_counter) && j != ctr_size; ++j) { | 
198  | 0  |             const size_t off = i * BS + (BS - 1 - j);  | 
199  | 0  |             const uint16_t cnt = static_cast<uint16_t>(m_counter[off]) + carry;  | 
200  | 0  |             m_counter[off] = static_cast<uint8_t>(cnt);  | 
201  | 0  |             local_counter = (local_counter >> 8);  | 
202  | 0  |             carry = (cnt >> 8) + static_cast<uint8_t>(local_counter);  | 
203  | 0  |          }  | 
204  | 0  |       }  | 
205  | 0  |    }  | 
206  | 0  | }  | 
207  |  |  | 
208  | 0  | void CTR_BE::seek(uint64_t offset) { | 
209  | 0  |    assert_key_material_set();  | 
210  |  | 
  | 
211  | 0  |    const uint64_t base_counter = m_ctr_blocks * (offset / m_counter.size());  | 
212  |  | 
  | 
213  | 0  |    zeroise(m_counter);  | 
214  | 0  |    BOTAN_ASSERT_NOMSG(m_counter.size() >= m_iv.size());  | 
215  | 0  |    copy_mem(&m_counter[0], &m_iv[0], m_iv.size());  | 
216  |  | 
  | 
217  | 0  |    const size_t BS = m_block_size;  | 
218  |  |  | 
219  |  |    // Set m_counter blocks to IV, IV + 1, ... IV + n  | 
220  |  | 
  | 
221  | 0  |    if(m_ctr_size == 4 && BS >= 8) { | 
222  | 0  |       const uint32_t low32 = load_be<uint32_t>(&m_counter[BS - 4], 0);  | 
223  |  | 
  | 
224  | 0  |       if(m_ctr_blocks >= 4 && is_power_of_2(m_ctr_blocks)) { | 
225  | 0  |          size_t written = 1;  | 
226  | 0  |          while(written < m_ctr_blocks) { | 
227  | 0  |             copy_mem(&m_counter[written * BS], &m_counter[0], BS * written);  | 
228  | 0  |             written *= 2;  | 
229  | 0  |          }  | 
230  | 0  |       } else { | 
231  | 0  |          for(size_t i = 1; i != m_ctr_blocks; ++i) { | 
232  | 0  |             copy_mem(&m_counter[i * BS], &m_counter[0], BS - 4);  | 
233  | 0  |          }  | 
234  | 0  |       }  | 
235  |  | 
  | 
236  | 0  |       for(size_t i = 1; i != m_ctr_blocks; ++i) { | 
237  | 0  |          const uint32_t c = static_cast<uint32_t>(low32 + i);  | 
238  | 0  |          store_be(c, &m_counter[(BS - 4) + i * BS]);  | 
239  | 0  |       }  | 
240  | 0  |    } else { | 
241  |  |       // do everything sequentially:  | 
242  | 0  |       for(size_t i = 1; i != m_ctr_blocks; ++i) { | 
243  | 0  |          copy_mem(&m_counter[i * BS], &m_counter[(i - 1) * BS], BS);  | 
244  |  | 
  | 
245  | 0  |          for(size_t j = 0; j != m_ctr_size; ++j) { | 
246  | 0  |             if(++m_counter[i * BS + (BS - 1 - j)]) { | 
247  | 0  |                break;  | 
248  | 0  |             }  | 
249  | 0  |          }  | 
250  | 0  |       }  | 
251  | 0  |    }  | 
252  |  | 
  | 
253  | 0  |    if(base_counter > 0) { | 
254  | 0  |       add_counter(base_counter);  | 
255  | 0  |    }  | 
256  |  | 
  | 
257  | 0  |    m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);  | 
258  | 0  |    m_pad_pos = offset % m_counter.size();  | 
259  | 0  | }  | 
260  |  | }  // namespace Botan  |