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