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

randombit / botan / 9348348007

03 Jun 2024 10:15AM UTC coverage: 91.822% (-0.002%) from 91.824%
9348348007

Pull #4094

github

web-flow
Merge efd9e69fc into 5649a10ec
Pull Request #4094: FIX: botan_cipher_get_update_granularity()

84472 of 91995 relevant lines covered (91.82%)

9216411.69 hits per line

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

97.2
/src/lib/ffi/ffi_cipher.cpp
1
/*
2
* (C) 2015,2017 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#include <botan/ffi.h>
8

9
#include <botan/aead.h>
10
#include <botan/internal/ffi_util.h>
11
#include <botan/internal/stl_util.h>
12

13
extern "C" {
14

15
using namespace Botan_FFI;
16

17
struct botan_cipher_struct final : public botan_struct<Botan::Cipher_Mode, 0xB4A2BF9C> {
18
   public:
19
      explicit botan_cipher_struct(std::unique_ptr<Botan::Cipher_Mode> x,
31✔
20
                                   size_t update_size,
21
                                   size_t ideal_update_size) :
31✔
22
            botan_struct(std::move(x)), m_update_size(update_size), m_ideal_update_size(ideal_update_size) {
31✔
23
         BOTAN_DEBUG_ASSERT(ideal_update_size >= update_size);
31✔
24
         m_buf.reserve(m_ideal_update_size);
31✔
25
      }
31✔
26

27
      Botan::secure_vector<uint8_t>& buf() { return m_buf; }
103✔
28

29
      size_t update_size() const { return m_update_size; }
92✔
30

31
      size_t ideal_update_size() const { return m_ideal_update_size; }
64✔
32

33
   private:
34
      Botan::secure_vector<uint8_t> m_buf;
35
      size_t m_update_size;
36
      size_t m_ideal_update_size;
37
};
38

39
int botan_cipher_init(botan_cipher_t* cipher, const char* cipher_name, uint32_t flags) {
31✔
40
   return ffi_guard_thunk(__func__, [=]() -> int {
31✔
41
      const bool encrypt_p = ((flags & BOTAN_CIPHER_INIT_FLAG_MASK_DIRECTION) == BOTAN_CIPHER_INIT_FLAG_ENCRYPT);
31✔
42
      const Botan::Cipher_Dir dir = encrypt_p ? Botan::Cipher_Dir::Encryption : Botan::Cipher_Dir::Decryption;
31✔
43

44
      std::unique_ptr<Botan::Cipher_Mode> mode(Botan::Cipher_Mode::create(cipher_name, dir));
31✔
45
      if(!mode) {
31✔
46
         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
47
      }
48

49
      const size_t update_size = mode->update_granularity();
31✔
50
      const size_t ideal_update_size = std::max(mode->ideal_granularity(), update_size);
31✔
51
      *cipher = new botan_cipher_struct(std::move(mode), update_size, ideal_update_size);
31✔
52
      return BOTAN_FFI_SUCCESS;
31✔
53
   });
62✔
54
}
55

56
int botan_cipher_destroy(botan_cipher_t cipher) {
31✔
57
   return BOTAN_FFI_CHECKED_DELETE(cipher);
31✔
58
}
59

60
int botan_cipher_clear(botan_cipher_t cipher) {
8✔
61
   return BOTAN_FFI_VISIT(cipher, [](auto& c) { c.clear(); });
16✔
62
}
63

64
int botan_cipher_reset(botan_cipher_t cipher) {
4✔
65
   return BOTAN_FFI_VISIT(cipher, [](auto& c) { c.reset(); });
8✔
66
}
67

68
int botan_cipher_output_length(botan_cipher_t cipher, size_t in_len, size_t* out_len) {
4✔
69
   if(out_len == nullptr) {
4✔
70
      return BOTAN_FFI_ERROR_NULL_POINTER;
71
   }
72

73
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { *out_len = c.output_length(in_len); });
8✔
74
}
75

76
int botan_cipher_query_keylen(botan_cipher_t cipher, size_t* out_minimum_keylength, size_t* out_maximum_keylength) {
13✔
77
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) {
26✔
78
      *out_minimum_keylength = c.key_spec().minimum_keylength();
79
      *out_maximum_keylength = c.key_spec().maximum_keylength();
80
   });
81
}
82

83
int botan_cipher_get_keyspec(botan_cipher_t cipher,
1✔
84
                             size_t* out_minimum_keylength,
85
                             size_t* out_maximum_keylength,
86
                             size_t* out_keylength_modulo) {
87
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) {
2✔
88
      if(out_minimum_keylength)
89
         *out_minimum_keylength = c.key_spec().minimum_keylength();
90
      if(out_maximum_keylength)
91
         *out_maximum_keylength = c.key_spec().maximum_keylength();
92
      if(out_keylength_modulo)
93
         *out_keylength_modulo = c.key_spec().keylength_multiple();
94
   });
95
}
96

97
int botan_cipher_set_key(botan_cipher_t cipher, const uint8_t* key, size_t key_len) {
43✔
98
   return BOTAN_FFI_VISIT(cipher, [=](auto& c) { c.set_key(key, key_len); });
86✔
99
}
100

101
int botan_cipher_start(botan_cipher_t cipher_obj, const uint8_t* nonce, size_t nonce_len) {
47✔
102
   return ffi_guard_thunk(__func__, [=]() -> int {
47✔
103
      Botan::Cipher_Mode& cipher = safe_get(cipher_obj);
47✔
104
      cipher.start(nonce, nonce_len);
47✔
105
      return BOTAN_FFI_SUCCESS;
47✔
106
   });
47✔
107
}
108

109
int botan_cipher_update(botan_cipher_t cipher_obj,
103✔
110
                        uint32_t flags,
111
                        uint8_t output[],
112
                        size_t output_size,
113
                        size_t* output_written,
114
                        const uint8_t input[],
115
                        size_t input_size,
116
                        size_t* input_consumed) {
117
   return ffi_guard_thunk(__func__, [=]() -> int {
103✔
118
      using namespace Botan;
103✔
119
      Cipher_Mode& cipher = safe_get(cipher_obj);
103✔
120
      secure_vector<uint8_t>& mbuf = cipher_obj->buf();
103✔
121

122
      // If the cipher object's internal buffer contains residual data from
123
      // a previous invocation, we can be sure that botan_cipher_update() was
124
      // called with the final flag set but not enough buffer space was provided
125
      // to accommodate the final output.
126
      const bool was_finished_before = !mbuf.empty();
103✔
127
      const bool final_input = (flags & BOTAN_CIPHER_UPDATE_FLAG_FINAL);
103✔
128

129
      // Bring the output variables into a defined state.
130
      *output_written = 0;
206✔
131
      *input_consumed = 0;
64✔
132

133
      // Once the final flag was set once, it must always be set for
134
      // consecutive invocations.
135
      if(was_finished_before && !final_input) {
103✔
136
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
137
      }
138

139
      // If the final flag was set in a previous invocation, no more input
140
      // data can be processed.
141
      if(was_finished_before && input_size > 0) {
103✔
142
         return BOTAN_FFI_ERROR_BAD_PARAMETER;
143
      }
144

145
      // Make sure that we always clear the internal buffer before returning
146
      // or aborting this invocation due to an exception.
147
      auto clean_buffer = scoped_cleanup([&mbuf] { mbuf.clear(); });
301✔
148

149
      if(final_input) {
103✔
150
         // If the final flag is set for the first time, we need to process the
151
         // remaining input data and then finalize the cipher object.
152
         if(!was_finished_before) {
39✔
153
            *input_consumed = input_size;
34✔
154
            mbuf.resize(input_size);
34✔
155
            copy_mem(mbuf, std::span(input, input_size));
34✔
156

157
            try {
34✔
158
               cipher.finish(mbuf);
34✔
159
            } catch(Invalid_Authentication_Tag&) {
×
160
               return BOTAN_FFI_ERROR_BAD_MAC;
×
161
            }
×
162
         }
163

164
         // At this point, the cipher object is finalized (potentially in a
165
         // previous invocation) and we can copy the final output to the caller.
166
         *output_written = mbuf.size();
39✔
167

168
         // Not enough space to copy the final output out to the caller.
169
         // Inform them how much space we need for a successful operation.
170
         if(output_size < mbuf.size()) {
39✔
171
            // This is the only place where mbuf is not cleared before returning.
172
            clean_buffer.disengage();
173
            return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
174
         }
175

176
         // Copy the final output to the caller, mbuf is cleared afterwards.
177
         copy_mem(std::span(output, mbuf.size()), mbuf);
34✔
178
      } else {
179
         // Process data in a streamed fashion without finalizing. No data is
180
         // ever retained in the cipher object's internal buffer. If we run out
181
         // of either input data or output capacity, we stop and report that not
182
         // all bytes were processed via *output_written and *input_consumed.
183

184
         BufferSlicer in({input, input_size});
64✔
185
         BufferStuffer out({output, output_size});
64✔
186

187
         // Helper function to do blockwise processing of data.
188
         auto blockwise_update = [&](const size_t granularity) {
192✔
189
            if(granularity == 0) {
128✔
190
               return;
191
            }
192

193
            const size_t expected_output_per_iteration = cipher.requires_entire_message() ? 0 : granularity;
86✔
194
            mbuf.resize(granularity);
264✔
195

196
            while(in.remaining() >= granularity && out.remaining_capacity() >= expected_output_per_iteration) {
284✔
197
               copy_mem(mbuf, in.take(granularity));
66✔
198
               const auto written_bytes = cipher.process(mbuf);
66✔
199
               BOTAN_DEBUG_ASSERT(written_bytes == expected_output_per_iteration);
66✔
200
               if(written_bytes > 0) {
66✔
201
                  BOTAN_ASSERT_NOMSG(written_bytes <= granularity);
46✔
202
                  copy_mem(out.next(written_bytes), std::span(mbuf).first(written_bytes));
46✔
203
               }
204
            }
205
         };
64✔
206

207
         // First, process as much data as possible in chunks of ideal granularity
208
         blockwise_update(cipher_obj->ideal_update_size());
64✔
209

210
         // Then process the remaining bytes in chunks of update_size() or, in one go
211
         // if update_size() is equal to 1 --> i.e. likely a stream cipher.
212
         const bool is_stream_cipher = (cipher_obj->update_size() == 1);
64✔
213
         const size_t tail_granularity =
64✔
214
            is_stream_cipher ? std::min(in.remaining(), out.remaining_capacity()) : cipher_obj->update_size();
64✔
215
         BOTAN_DEBUG_ASSERT(tail_granularity < cipher_obj->ideal_update_size());
64✔
216
         blockwise_update(tail_granularity);
64✔
217

218
         // Inform the caller about the amount of data processed.
219
         *output_written = output_size - out.remaining_capacity();
64✔
220
         *input_consumed = input_size - in.remaining();
64✔
221
      }
222

223
      return BOTAN_FFI_SUCCESS;
224
   });
206✔
225
}
226

227
int botan_cipher_set_associated_data(botan_cipher_t cipher, const uint8_t* ad, size_t ad_len) {
8✔
228
   return BOTAN_FFI_VISIT(cipher, [=](auto& c) {
16✔
229
      if(Botan::AEAD_Mode* aead = dynamic_cast<Botan::AEAD_Mode*>(&c)) {
230
         aead->set_associated_data(ad, ad_len);
231
         return BOTAN_FFI_SUCCESS;
232
      }
233
      return BOTAN_FFI_ERROR_BAD_PARAMETER;
234
   });
235
}
236

237
int botan_cipher_valid_nonce_length(botan_cipher_t cipher, size_t nl) {
6✔
238
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { return c.valid_nonce_length(nl) ? 1 : 0; });
12✔
239
}
240

241
int botan_cipher_get_default_nonce_length(botan_cipher_t cipher, size_t* nl) {
12✔
242
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { *nl = c.default_nonce_length(); });
24✔
243
}
244

245
int botan_cipher_get_update_granularity(botan_cipher_t cipher, size_t* ug) {
28✔
246
   return BOTAN_FFI_VISIT(cipher, [=](const auto& /*c*/) { *ug = cipher->update_size(); });
28✔
247
}
248

249
int botan_cipher_get_ideal_update_granularity(botan_cipher_t cipher, size_t* ug) {
28✔
250
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { *ug = c.ideal_granularity(); });
56✔
251
}
252

253
int botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tl) {
16✔
254
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { *tl = c.tag_size(); });
32✔
255
}
256

257
int botan_cipher_is_authenticated(botan_cipher_t cipher) {
15✔
258
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { return c.authenticated() ? 1 : 0; });
30✔
259
}
260

261
int botan_cipher_requires_entire_message(botan_cipher_t cipher) {
5✔
262
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { return c.requires_entire_message() ? 1 : 0; });
10✔
263
}
264

265
int botan_cipher_name(botan_cipher_t cipher, char* name, size_t* name_len) {
8✔
266
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { return write_str_output(name, name_len, c.name()); });
20✔
267
}
268
}
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