1111#include < botan/internal/ct_utils.h>
1212#include < botan/internal/fmt.h>
1313#include < botan/internal/loadstor.h>
14+ #include < botan/internal/stl_util.h>
1415
1516namespace Botan {
1617
@@ -168,24 +169,28 @@ secure_vector<uint8_t> CCM_Mode::format_c0() {
168169 return C;
169170}
170171
171- void CCM_Encryption::finish_msg (secure_vector<uint8_t >& buffer, size_t offset) {
172- BOTAN_ARG_CHECK (buffer.size () >= offset, " Offset is out of range" );
172+ size_t CCM_Encryption::finish_msg (std::span<uint8_t > buffer, size_t input_bytes) {
173+ const auto tag_length = tag_size ();
174+ const auto & buffered = msg_buf ();
173175
174- buffer. insert (buffer. begin () + offset, msg_buf (). begin (), msg_buf (). end ());
176+ BOTAN_ASSERT_NOMSG (buffered. size () + input_bytes + tag_length == buffer. size ());
175177
176- const size_t sz = buffer.size () - offset ;
177- uint8_t * buf = buffer.data () + offset ;
178+ const auto entire_payload = buffer.first (buffered. size () + input_bytes) ;
179+ const auto tag = buffer.last (tag_length) ;
178180
179181 const secure_vector<uint8_t >& ad = ad_buf ();
180182 BOTAN_ARG_CHECK (ad.size () % CCM_BS == 0 , " AD is block size multiple" );
181183
182184 const BlockCipher& E = cipher ();
183185
186+ // TODO: consider using std::array<> for all those block-size'ed buffers
187+ // (this requires adapting more helper functions like `format_b0`, ...)
184188 secure_vector<uint8_t > T (CCM_BS);
185- E.encrypt (format_b0 (sz ), T);
189+ E.encrypt (format_b0 (entire_payload. size () ), T);
186190
187- for (size_t i = 0 ; i != ad.size (); i += CCM_BS) {
188- xor_buf (T.data (), &ad[i], CCM_BS);
191+ BufferSlicer ad_bs (ad);
192+ while (!ad_bs.empty ()) {
193+ xor_buf (T, ad_bs.take (CCM_BS));
189194 E.encrypt (T);
190195 }
191196
@@ -196,45 +201,56 @@ void CCM_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
196201
197202 secure_vector<uint8_t > X (CCM_BS);
198203
199- const uint8_t * buf_end = &buf[sz];
204+ // copy all buffered input into the in/out buffer if needed
205+ if (!buffered.empty ()) {
206+ copy_mem (entire_payload.last (input_bytes), entire_payload.first (input_bytes));
207+ copy_mem (entire_payload.first (buffered.size ()), buffered);
208+ }
209+
210+ // TODO: Use BufferTransformer, once it is available
211+ // See https://github.com/randombit/botan/pull/4151
212+ BufferSlicer payload_slicer (entire_payload);
213+ BufferStuffer payload_stuffer (entire_payload);
200214
201- while (buf != buf_end) {
202- const size_t to_proc = std::min<size_t >(CCM_BS, buf_end - buf);
215+ while (!payload_slicer.empty ()) {
216+ const size_t to_proc = std::min<size_t >(CCM_BS, payload_slicer.remaining ());
217+ const auto in_chunk = payload_slicer.take (to_proc);
218+ const auto out_chunk = payload_stuffer.next (to_proc);
203219
204- xor_buf (T. data (), buf, to_proc );
220+ xor_buf (std::span{T}. first (in_chunk. size ()), in_chunk );
205221 E.encrypt (T);
206222
207223 E.encrypt (C, X);
208- xor_buf (buf, X. data (), to_proc );
224+ xor_buf (out_chunk, std::span{X}. first (out_chunk. size ()) );
209225 inc (C);
210-
211- buf += to_proc;
212226 }
213227
214228 T ^= S0;
215229
216- buffer += std::make_pair (T.data (), tag_size ());
230+ BOTAN_DEBUG_ASSERT (tag.size () <= T.size ());
231+ copy_mem (tag, std::span{T}.first (tag.size ()));
217232
218233 reset ();
219- }
220234
221- void CCM_Decryption::finish_msg (secure_vector< uint8_t >& buffer, size_t offset) {
222- BOTAN_ARG_CHECK (buffer. size () >= offset, " Offset is out of range " );
235+ return buffer. size ();
236+ }
223237
224- buffer.insert (buffer.begin () + offset, msg_buf ().begin (), msg_buf ().end ());
238+ size_t CCM_Decryption::finish_msg (std::span<uint8_t > buffer, size_t input_bytes) {
239+ const auto tag_length = tag_size ();
240+ const auto & buffered = msg_buf ();
225241
226- const size_t sz = buffer.size () - offset;
227- uint8_t * buf = buffer.data () + offset;
242+ BOTAN_ASSERT_NOMSG (buffered.size () + input_bytes == buffer.size ());
228243
229- BOTAN_ARG_CHECK (sz >= tag_size (), " input did not include the tag" );
244+ const auto entire_payload = buffer.first (buffer.size () - tag_length);
245+ const auto tag = buffer.last (tag_length);
230246
231247 const secure_vector<uint8_t >& ad = ad_buf ();
232248 BOTAN_ARG_CHECK (ad.size () % CCM_BS == 0 , " AD is block size multiple" );
233249
234250 const BlockCipher& E = cipher ();
235251
236252 secure_vector<uint8_t > T (CCM_BS);
237- E.encrypt (format_b0 (sz - tag_size ()), T);
253+ E.encrypt (format_b0 (entire_payload. size ()), T);
238254
239255 for (size_t i = 0 ; i != ad.size (); i += CCM_BS) {
240256 xor_buf (T.data (), &ad[i], CCM_BS);
@@ -249,30 +265,39 @@ void CCM_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
249265
250266 secure_vector<uint8_t > X (CCM_BS);
251267
252- const uint8_t * buf_end = &buf[sz - tag_size ()];
268+ // copy all buffered input into the in/out buffer if needed
269+ if (!buffered.empty ()) {
270+ copy_mem (buffer.last (input_bytes), buffer.first (input_bytes));
271+ copy_mem (buffer.first (buffered.size ()), buffered);
272+ }
273+
274+ // TODO: Use BufferTransformer, once it is available
275+ // See https://github.com/randombit/botan/pull/4151
276+ BufferSlicer payload_slicer (entire_payload);
277+ BufferStuffer payload_stuffer (entire_payload);
253278
254- while (buf != buf_end) {
255- const size_t to_proc = std::min<size_t >(CCM_BS, buf_end - buf);
279+ while (!payload_slicer.empty ()) {
280+ const size_t to_proc = std::min<size_t >(CCM_BS, payload_slicer.remaining ());
281+ const auto in_chunk = payload_slicer.take (to_proc);
282+ const auto out_chunk = payload_stuffer.next (to_proc);
256283
257284 E.encrypt (C, X);
258- xor_buf (buf, X. data (), to_proc );
285+ xor_buf (out_chunk, std::span{X}. first (out_chunk. size ()) );
259286 inc (C);
260287
261- xor_buf (T. data (), buf, to_proc );
288+ xor_buf (std::span{T}. first (in_chunk. size ()), in_chunk );
262289 E.encrypt (T);
263-
264- buf += to_proc;
265290 }
266291
267292 T ^= S0;
268293
269- if (!CT::is_equal (T.data (), buf_end, tag_size ()).as_bool ()) {
294+ if (!CT::is_equal (T.data (), tag. data (), tag. size ()).as_bool ()) {
270295 throw Invalid_Authentication_Tag (" CCM tag check failed" );
271296 }
272297
273- buffer.resize (buffer.size () - tag_size ());
274-
275298 reset ();
299+
300+ return entire_payload.size ();
276301}
277302
278303} // namespace Botan
0 commit comments