/src/botan/src/lib/modes/cfb/cfb.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * CFB Mode |
3 | | * (C) 1999-2007,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/cfb.h> |
10 | | |
11 | | #include <botan/internal/fmt.h> |
12 | | |
13 | | namespace Botan { |
14 | | |
15 | | CFB_Mode::CFB_Mode(std::unique_ptr<BlockCipher> cipher, size_t feedback_bits) : |
16 | 0 | m_cipher(std::move(cipher)), |
17 | 0 | m_block_size(m_cipher->block_size()), |
18 | 0 | m_feedback_bytes(feedback_bits ? feedback_bits / 8 : m_block_size) { |
19 | 0 | if(feedback_bits % 8 || feedback() > m_block_size) { |
20 | 0 | throw Invalid_Argument(fmt("{} does not support feedback bits of {}", name(), feedback_bits)); |
21 | 0 | } |
22 | 0 | } |
23 | | |
24 | 0 | void CFB_Mode::clear() { |
25 | 0 | m_cipher->clear(); |
26 | 0 | m_keystream.clear(); |
27 | 0 | reset(); |
28 | 0 | } |
29 | | |
30 | 0 | void CFB_Mode::reset() { |
31 | 0 | m_state.clear(); |
32 | 0 | zeroise(m_keystream); |
33 | 0 | } |
34 | | |
35 | 0 | std::string CFB_Mode::name() const { |
36 | 0 | if(feedback() == cipher().block_size()) { |
37 | 0 | return fmt("{}/CFB", cipher().name()); |
38 | 0 | } else { |
39 | 0 | return fmt("{}/CFB({})", cipher().name(), feedback() * 8); |
40 | 0 | } |
41 | 0 | } |
42 | | |
43 | 0 | size_t CFB_Mode::output_length(size_t input_length) const { |
44 | 0 | return input_length; |
45 | 0 | } |
46 | | |
47 | 0 | size_t CFB_Mode::update_granularity() const { |
48 | 0 | return feedback(); |
49 | 0 | } |
50 | | |
51 | 0 | size_t CFB_Mode::ideal_granularity() const { |
52 | | // Multiplier here is arbitrary |
53 | 0 | return 16 * feedback(); |
54 | 0 | } |
55 | | |
56 | 0 | size_t CFB_Mode::minimum_final_size() const { |
57 | 0 | return 0; |
58 | 0 | } |
59 | | |
60 | 0 | Key_Length_Specification CFB_Mode::key_spec() const { |
61 | 0 | return cipher().key_spec(); |
62 | 0 | } |
63 | | |
64 | 0 | size_t CFB_Mode::default_nonce_length() const { |
65 | 0 | return block_size(); |
66 | 0 | } |
67 | | |
68 | 0 | bool CFB_Mode::valid_nonce_length(size_t n) const { |
69 | 0 | return (n == 0 || n == block_size()); |
70 | 0 | } |
71 | | |
72 | 0 | bool CFB_Mode::has_keying_material() const { |
73 | 0 | return m_cipher->has_keying_material(); |
74 | 0 | } |
75 | | |
76 | 0 | void CFB_Mode::key_schedule(std::span<const uint8_t> key) { |
77 | 0 | m_cipher->set_key(key); |
78 | 0 | m_keystream.resize(m_cipher->block_size()); |
79 | 0 | } |
80 | | |
81 | 0 | void CFB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) { |
82 | 0 | if(!valid_nonce_length(nonce_len)) { |
83 | 0 | throw Invalid_IV_Length(name(), nonce_len); |
84 | 0 | } |
85 | | |
86 | 0 | assert_key_material_set(); |
87 | |
|
88 | 0 | if(nonce_len == 0) { |
89 | 0 | if(m_state.empty()) { |
90 | 0 | throw Invalid_State("CFB requires a non-empty initial nonce"); |
91 | 0 | } |
92 | | // No reason to encrypt state->keystream_buf, because no change |
93 | 0 | } else { |
94 | 0 | m_state.assign(nonce, nonce + nonce_len); |
95 | 0 | cipher().encrypt(m_state, m_keystream); |
96 | 0 | m_keystream_pos = 0; |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | 0 | void CFB_Mode::shift_register() { |
101 | 0 | const size_t shift = feedback(); |
102 | 0 | const size_t carryover = block_size() - shift; |
103 | |
|
104 | 0 | if(carryover > 0) { |
105 | 0 | copy_mem(m_state.data(), &m_state[shift], carryover); |
106 | 0 | } |
107 | 0 | copy_mem(&m_state[carryover], m_keystream.data(), shift); |
108 | 0 | cipher().encrypt(m_state, m_keystream); |
109 | 0 | m_keystream_pos = 0; |
110 | 0 | } |
111 | | |
112 | 0 | size_t CFB_Encryption::process_msg(uint8_t buf[], size_t sz) { |
113 | 0 | assert_key_material_set(); |
114 | 0 | BOTAN_STATE_CHECK(m_state.empty() == false); |
115 | |
|
116 | 0 | const size_t shift = feedback(); |
117 | |
|
118 | 0 | size_t left = sz; |
119 | |
|
120 | 0 | if(m_keystream_pos != 0) { |
121 | 0 | const size_t take = std::min<size_t>(left, shift - m_keystream_pos); |
122 | |
|
123 | 0 | xor_buf(m_keystream.data() + m_keystream_pos, buf, take); |
124 | 0 | copy_mem(buf, m_keystream.data() + m_keystream_pos, take); |
125 | |
|
126 | 0 | m_keystream_pos += take; |
127 | 0 | left -= take; |
128 | 0 | buf += take; |
129 | |
|
130 | 0 | if(m_keystream_pos == shift) { |
131 | 0 | shift_register(); |
132 | 0 | } |
133 | 0 | } |
134 | |
|
135 | 0 | while(left >= shift) { |
136 | 0 | xor_buf(m_keystream.data(), buf, shift); |
137 | 0 | copy_mem(buf, m_keystream.data(), shift); |
138 | |
|
139 | 0 | left -= shift; |
140 | 0 | buf += shift; |
141 | 0 | shift_register(); |
142 | 0 | } |
143 | |
|
144 | 0 | if(left > 0) { |
145 | 0 | xor_buf(m_keystream.data(), buf, left); |
146 | 0 | copy_mem(buf, m_keystream.data(), left); |
147 | 0 | m_keystream_pos += left; |
148 | 0 | } |
149 | |
|
150 | 0 | return sz; |
151 | 0 | } |
152 | | |
153 | 0 | void CFB_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) { |
154 | 0 | update(buffer, offset); |
155 | 0 | } |
156 | | |
157 | | namespace { |
158 | | |
159 | 0 | inline void xor_copy(uint8_t buf[], uint8_t key_buf[], size_t len) { |
160 | 0 | for(size_t i = 0; i != len; ++i) { |
161 | 0 | uint8_t k = key_buf[i]; |
162 | 0 | key_buf[i] = buf[i]; |
163 | 0 | buf[i] ^= k; |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | | } // namespace |
168 | | |
169 | 0 | size_t CFB_Decryption::process_msg(uint8_t buf[], size_t sz) { |
170 | 0 | assert_key_material_set(); |
171 | 0 | BOTAN_STATE_CHECK(m_state.empty() == false); |
172 | |
|
173 | 0 | const size_t shift = feedback(); |
174 | |
|
175 | 0 | size_t left = sz; |
176 | |
|
177 | 0 | if(m_keystream_pos != 0) { |
178 | 0 | const size_t take = std::min<size_t>(left, shift - m_keystream_pos); |
179 | |
|
180 | 0 | xor_copy(buf, m_keystream.data() + m_keystream_pos, take); |
181 | |
|
182 | 0 | m_keystream_pos += take; |
183 | 0 | left -= take; |
184 | 0 | buf += take; |
185 | |
|
186 | 0 | if(m_keystream_pos == shift) { |
187 | 0 | shift_register(); |
188 | 0 | } |
189 | 0 | } |
190 | |
|
191 | 0 | while(left >= shift) { |
192 | 0 | xor_copy(buf, m_keystream.data(), shift); |
193 | 0 | left -= shift; |
194 | 0 | buf += shift; |
195 | 0 | shift_register(); |
196 | 0 | } |
197 | |
|
198 | 0 | if(left > 0) { |
199 | 0 | xor_copy(buf, m_keystream.data(), left); |
200 | 0 | m_keystream_pos += left; |
201 | 0 | } |
202 | |
|
203 | 0 | return sz; |
204 | 0 | } |
205 | | |
206 | 0 | void CFB_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) { |
207 | 0 | update(buffer, offset); |
208 | 0 | } |
209 | | |
210 | | } // namespace Botan |