Coverage Report

Created: 2025-08-29 06:23

/src/Botan-3.4.0/src/lib/ffi/ffi_cipher.cpp
Line
Count
Source (jump to first uncovered line)
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
12
extern "C" {
13
14
using namespace Botan_FFI;
15
16
struct botan_cipher_struct final : public botan_struct<Botan::Cipher_Mode, 0xB4A2BF9C> {
17
   public:
18
      explicit botan_cipher_struct(std::unique_ptr<Botan::Cipher_Mode> x, size_t update_size) :
19
0
            botan_struct(std::move(x)), m_update_size(update_size) {
20
0
         m_buf.reserve(m_update_size);
21
0
      }
22
23
0
      Botan::secure_vector<uint8_t>& buf() { return m_buf; }
24
25
0
      size_t update_size() const { return m_update_size; }
26
27
   private:
28
      Botan::secure_vector<uint8_t> m_buf;
29
      size_t m_update_size;
30
};
31
32
namespace {
33
34
0
size_t ffi_choose_update_size(Botan::Cipher_Mode& mode) {
35
0
   const size_t update_granularity = mode.update_granularity();
36
0
   const size_t minimum_final_size = mode.minimum_final_size();
37
38
   /*
39
   * Return the minimum possible granularity given the FFI API constraints that
40
   * we require the returned size be > minimum final size.
41
   *
42
   * If the minimum final size is zero, or the update_granularity is
43
   * already greater, just use that.
44
   *
45
   * Otherwise scale the update_granularity to a sufficient size
46
   * to be greater than the minimum.
47
   */
48
0
   if(minimum_final_size == 0 || update_granularity > minimum_final_size) {
49
0
      BOTAN_ASSERT_NOMSG(update_granularity > 0);
50
0
      return update_granularity;
51
0
   }
52
53
0
   size_t buf_size = std::max(update_granularity, minimum_final_size + 1);
54
0
   if(buf_size % update_granularity != 0) {
55
0
      buf_size += update_granularity - (buf_size % update_granularity);
56
0
   }
57
58
0
   return buf_size;
59
0
}
60
61
}  // namespace
62
63
0
int botan_cipher_init(botan_cipher_t* cipher, const char* cipher_name, uint32_t flags) {
64
0
   return ffi_guard_thunk(__func__, [=]() -> int {
65
0
      const bool encrypt_p = ((flags & BOTAN_CIPHER_INIT_FLAG_MASK_DIRECTION) == BOTAN_CIPHER_INIT_FLAG_ENCRYPT);
66
0
      const Botan::Cipher_Dir dir = encrypt_p ? Botan::Cipher_Dir::Encryption : Botan::Cipher_Dir::Decryption;
67
68
0
      std::unique_ptr<Botan::Cipher_Mode> mode(Botan::Cipher_Mode::create(cipher_name, dir));
69
0
      if(!mode) {
70
0
         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
71
0
      }
72
73
0
      const size_t update_size = ffi_choose_update_size(*mode);
74
75
0
      *cipher = new botan_cipher_struct(std::move(mode), update_size);
76
0
      return BOTAN_FFI_SUCCESS;
77
0
   });
78
0
}
79
80
0
int botan_cipher_destroy(botan_cipher_t cipher) {
81
0
   return BOTAN_FFI_CHECKED_DELETE(cipher);
82
0
}
83
84
0
int botan_cipher_clear(botan_cipher_t cipher) {
85
0
   return BOTAN_FFI_VISIT(cipher, [](auto& c) { c.clear(); });
86
0
}
87
88
0
int botan_cipher_reset(botan_cipher_t cipher) {
89
0
   return BOTAN_FFI_VISIT(cipher, [](auto& c) { c.reset(); });
90
0
}
91
92
0
int botan_cipher_output_length(botan_cipher_t cipher, size_t in_len, size_t* out_len) {
93
0
   if(out_len == nullptr) {
94
0
      return BOTAN_FFI_ERROR_NULL_POINTER;
95
0
   }
96
97
0
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { *out_len = c.output_length(in_len); });
98
0
}
99
100
0
int botan_cipher_query_keylen(botan_cipher_t cipher, size_t* out_minimum_keylength, size_t* out_maximum_keylength) {
101
0
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) {
102
0
      *out_minimum_keylength = c.key_spec().minimum_keylength();
103
0
      *out_maximum_keylength = c.key_spec().maximum_keylength();
104
0
   });
105
0
}
106
107
int botan_cipher_get_keyspec(botan_cipher_t cipher,
108
                             size_t* out_minimum_keylength,
109
                             size_t* out_maximum_keylength,
110
0
                             size_t* out_keylength_modulo) {
111
0
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) {
112
0
      if(out_minimum_keylength)
113
0
         *out_minimum_keylength = c.key_spec().minimum_keylength();
114
0
      if(out_maximum_keylength)
115
0
         *out_maximum_keylength = c.key_spec().maximum_keylength();
116
0
      if(out_keylength_modulo)
117
0
         *out_keylength_modulo = c.key_spec().keylength_multiple();
118
0
   });
119
0
}
120
121
0
int botan_cipher_set_key(botan_cipher_t cipher, const uint8_t* key, size_t key_len) {
122
0
   return BOTAN_FFI_VISIT(cipher, [=](auto& c) { c.set_key(key, key_len); });
123
0
}
124
125
0
int botan_cipher_start(botan_cipher_t cipher_obj, const uint8_t* nonce, size_t nonce_len) {
126
0
   return ffi_guard_thunk(__func__, [=]() -> int {
127
0
      Botan::Cipher_Mode& cipher = safe_get(cipher_obj);
128
0
      cipher.start(nonce, nonce_len);
129
0
      return BOTAN_FFI_SUCCESS;
130
0
   });
131
0
}
132
133
int botan_cipher_update(botan_cipher_t cipher_obj,
134
                        uint32_t flags,
135
                        uint8_t output_ptr[],
136
                        size_t orig_output_size,
137
                        size_t* output_written,
138
                        const uint8_t input_ptr[],
139
                        size_t orig_input_size,
140
0
                        size_t* input_consumed) {
141
0
   return ffi_guard_thunk(__func__, [=]() -> int {
142
0
      size_t input_size = orig_input_size;
143
0
      size_t output_size = orig_output_size;
144
0
      const uint8_t* input = input_ptr;
145
0
      uint8_t* output = output_ptr;
146
147
0
      using namespace Botan;
148
0
      Cipher_Mode& cipher = safe_get(cipher_obj);
149
0
      secure_vector<uint8_t>& mbuf = cipher_obj->buf();
150
151
0
      const bool final_input = (flags & BOTAN_CIPHER_UPDATE_FLAG_FINAL);
152
153
0
      if(final_input) {
154
0
         mbuf.assign(input, input + input_size);
155
0
         *input_consumed = input_size;
156
0
         *output_written = 0;
157
158
0
         try {
159
0
            cipher.finish(mbuf);
160
0
         } catch(Invalid_Authentication_Tag&) {
161
0
            return BOTAN_FFI_ERROR_BAD_MAC;
162
0
         }
163
164
0
         *output_written = mbuf.size();
165
166
0
         if(mbuf.size() <= output_size) {
167
0
            copy_mem(output, mbuf.data(), mbuf.size());
168
0
            mbuf.clear();
169
0
            return BOTAN_FFI_SUCCESS;
170
0
         }
171
172
0
         return -1;
173
0
      }
174
175
0
      if(input_size == 0) {
176
         // Currently must take entire buffer in this case
177
0
         *output_written = mbuf.size();
178
0
         if(output_size >= mbuf.size()) {
179
0
            copy_mem(output, mbuf.data(), mbuf.size());
180
0
            mbuf.clear();
181
0
            return BOTAN_FFI_SUCCESS;
182
0
         }
183
184
0
         return -1;
185
0
      }
186
187
0
      const size_t ud = cipher_obj->update_size();
188
189
0
      mbuf.resize(ud);
190
0
      size_t taken = 0, written = 0;
191
192
0
      while(input_size >= ud && output_size >= ud) {
193
0
         copy_mem(mbuf.data(), input, ud);
194
0
         const size_t bytes_produced = cipher.process(mbuf);
195
196
0
         input_size -= ud;
197
0
         input += ud;
198
0
         taken += ud;
199
200
0
         if(bytes_produced > 0) {
201
0
            copy_mem(output, mbuf.data(), bytes_produced);
202
0
            output_size -= bytes_produced;
203
0
            output += bytes_produced;
204
0
            written += bytes_produced;
205
0
         }
206
0
      }
207
208
0
      *output_written = written;
209
0
      *input_consumed = taken;
210
211
0
      return BOTAN_FFI_SUCCESS;
212
0
   });
213
0
}
214
215
0
int botan_cipher_set_associated_data(botan_cipher_t cipher, const uint8_t* ad, size_t ad_len) {
216
0
   return BOTAN_FFI_VISIT(cipher, [=](auto& c) {
217
0
      if(Botan::AEAD_Mode* aead = dynamic_cast<Botan::AEAD_Mode*>(&c)) {
218
0
         aead->set_associated_data(ad, ad_len);
219
0
         return BOTAN_FFI_SUCCESS;
220
0
      }
221
0
      return BOTAN_FFI_ERROR_BAD_PARAMETER;
222
0
   });
223
0
}
224
225
0
int botan_cipher_valid_nonce_length(botan_cipher_t cipher, size_t nl) {
226
0
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { return c.valid_nonce_length(nl) ? 1 : 0; });
227
0
}
228
229
0
int botan_cipher_get_default_nonce_length(botan_cipher_t cipher, size_t* nl) {
230
0
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { *nl = c.default_nonce_length(); });
231
0
}
232
233
0
int botan_cipher_get_update_granularity(botan_cipher_t cipher, size_t* ug) {
234
0
   return BOTAN_FFI_VISIT(cipher, [=](const auto& /*c*/) { *ug = cipher->update_size(); });
235
0
}
236
237
0
int botan_cipher_get_ideal_update_granularity(botan_cipher_t cipher, size_t* ug) {
238
0
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { *ug = c.ideal_granularity(); });
239
0
}
240
241
0
int botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tl) {
242
0
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { *tl = c.tag_size(); });
243
0
}
244
245
0
int botan_cipher_is_authenticated(botan_cipher_t cipher) {
246
0
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { return c.authenticated() ? 1 : 0; });
247
0
}
248
249
0
int botan_cipher_requires_entire_message(botan_cipher_t cipher) {
250
0
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { return c.requires_entire_message() ? 1 : 0; });
251
0
}
252
253
0
int botan_cipher_name(botan_cipher_t cipher, char* name, size_t* name_len) {
254
0
   return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { return write_str_output(name, name_len, c.name()); });
255
0
}
256
}