@@ -166,6 +166,8 @@ namespace {
166166
167167constexpr std::string_view AES_128_CBC = " aes-128-cbc" ;
168168constexpr std::string_view AES_256_CBC = " aes-256-cbc" ;
169+ constexpr std::string_view AES_128_GCM = " aes-128-gcm" ;
170+ constexpr std::string_view AES_256_GCM = " aes-256-gcm" ;
169171
170172std::optional<tl::CipherAlgorithm> parse_cipher_algorithm (const string& method) noexcept {
171173 using namespace std ::string_view_literals;
@@ -174,28 +176,45 @@ std::optional<tl::CipherAlgorithm> parse_cipher_algorithm(const string& method)
174176 const auto ichar_equals = [](char a, char b) { return std::tolower (a) == std::tolower (b); };
175177
176178 if (std::ranges::equal (method_sv, AES_128_CBC, ichar_equals)) {
177- return tl::CipherAlgorithm::AES128 ;
179+ return tl::CipherAlgorithm::AES128_CBC ;
178180 } else if (std::ranges::equal (method_sv, AES_256_CBC, ichar_equals)) {
179- return tl::CipherAlgorithm::AES256;
181+ return tl::CipherAlgorithm::AES256_CBC;
182+ } else if (std::ranges::equal (method_sv, AES_128_GCM, ichar_equals)) {
183+ return tl::CipherAlgorithm::AES128_GCM;
184+ } else if (std::ranges::equal (method_sv, AES_256_GCM, ichar_equals)) {
185+ return tl::CipherAlgorithm::AES256_GCM;
180186 }
181- return {} ;
187+ return std:: nullopt ;
182188}
183189
190+ bool is_gcm_algorithm (tl::CipherAlgorithm algorithm) {
191+ return algorithm == tl::AES128_GCM || algorithm == tl::AES256_GCM;
192+ }
193+
194+ bool is_aead_algorithm (tl::CipherAlgorithm algorithm) {
195+ return is_gcm_algorithm (algorithm);
196+ }
197+
198+ constexpr size_t GCM_NONCE_LEN = 12 ;
184199constexpr size_t AES_BLOCK_LEN = 16 ;
185200constexpr size_t AES_128_KEY_LEN = 16 ;
186201constexpr size_t AES_256_KEY_LEN = 32 ;
187202
188203int64_t algorithm_iv_len ([[maybe_unused]] tl::CipherAlgorithm algorithm) noexcept {
189- /* since only aes-128/256-cbc supported for now */
204+ if (is_gcm_algorithm (algorithm)) {
205+ return GCM_NONCE_LEN;
206+ }
190207 return AES_BLOCK_LEN;
191208}
192209
193210int64_t algorithm_key_len (tl::CipherAlgorithm algorithm) noexcept {
194211 switch (algorithm) {
195- case tl::CipherAlgorithm::AES128: {
212+ case tl::CipherAlgorithm::AES128_GCM:
213+ case tl::CipherAlgorithm::AES128_CBC: {
196214 return AES_128_KEY_LEN;
197215 }
198- case tl::CipherAlgorithm::AES256: {
216+ case tl::CipherAlgorithm::AES256_GCM:
217+ case tl::CipherAlgorithm::AES256_CBC: {
199218 return AES_256_KEY_LEN;
200219 }
201220 default : {
@@ -212,12 +231,19 @@ Optional<std::pair<string, string>> algorithm_pad_key_iv(tl::CipherAlgorithm alg
212231 const size_t iv_required_len = algorithm_iv_len (algorithm);
213232 const size_t key_required_len = algorithm_key_len (algorithm);
214233 auto iv = source_iv;
215- if (iv.size () < iv_required_len) {
216- kphp::log::warning (" IV passed is only {} bytes long, cipher expects an IV of precisely {} bytes, padding with \\ 0" , iv.size (), iv_required_len);
217- iv.append (static_cast <string::size_type>(iv_required_len - iv.size ()), ' \0 ' );
218- } else if (iv.size () > iv_required_len) {
219- kphp::log::warning (" IV passed is {} bytes long which is longer than the {} expected by selected cipher, truncating" , iv.size (), iv_required_len);
220- iv.shrink (static_cast <string::size_type>(iv_required_len));
234+ if (is_aead_algorithm (algorithm)) {
235+ if (source_iv.empty ()) {
236+ kphp::log::warning (" Setting of IV length for AEAD mode failed" );
237+ return false ;
238+ }
239+ } else {
240+ if (iv.size () < iv_required_len) {
241+ kphp::log::warning (" IV passed is only {} bytes long, cipher expects an IV of precisely {} bytes, padding with \\ 0" , iv.size (), iv_required_len);
242+ iv.append (static_cast <string::size_type>(iv_required_len - iv.size ()), ' \0 ' );
243+ } else if (iv.size () > iv_required_len) {
244+ kphp::log::warning (" IV passed is {} bytes long which is longer than the {} expected by selected cipher, truncating" , iv.size (), iv_required_len);
245+ iv.shrink (static_cast <string::size_type>(iv_required_len));
246+ }
221247 }
222248
223249 auto key = source_key;
@@ -234,25 +260,22 @@ Optional<std::pair<string, string>> algorithm_pad_key_iv(tl::CipherAlgorithm alg
234260 key.shrink (static_cast <string::size_type>(key_required_len));
235261 }
236262
237- if (options & static_cast <int64_t >(cipher_opts::OPENSSL_ZERO_PADDING)) {
238- kphp::log::warning (" OPENSSL_ZERO_PADDING option is not supported for now\n " );
239- return false ;
240- }
241263 return std::make_pair (key, iv);
242264}
243265
244266} // namespace
245267
246268array<string> f$openssl_get_cipher_methods([[maybe_unused]] bool aliases) noexcept {
247269 array<string> return_value{
248- {std::make_pair (0 , string{AES_128_CBC.data (), AES_128_CBC.size ()}), std::make_pair (1 , string{AES_256_CBC.data (), AES_256_CBC.size ()})}};
270+ {std::make_pair (0 , string{AES_128_CBC.data (), AES_128_CBC.size ()}), std::make_pair (1 , string{AES_256_CBC.data (), AES_256_CBC.size ()}),
271+ std::make_pair (2 , string{AES_128_GCM.data (), AES_128_GCM.size ()}), std::make_pair (3 , string{AES_256_GCM.data (), AES_256_GCM.size ()})}};
249272 return return_value;
250273}
251274
252275Optional<int64_t > f$openssl_cipher_iv_length(const string& method) noexcept {
253276 auto algorithm{parse_cipher_algorithm (method)};
254277 if (!algorithm) {
255- kphp::log::warning (" Unknown cipher algorithm" );
278+ kphp::log::warning (" Unknown cipher algorithm {} " , method. c_str () );
256279 return false ;
257280 }
258281 return algorithm_iv_len (*algorithm);
@@ -263,32 +286,43 @@ kphp::coro::task<Optional<string>> f$openssl_encrypt(string data, string method,
263286 [[maybe_unused]] int64_t tag_length) noexcept {
264287 auto algorithm{parse_cipher_algorithm (method)};
265288 if (!algorithm) {
266- kphp::log::warning (" Unknown cipher algorithm" );
289+ kphp::log::warning (" Unknown cipher algorithm {} " , method. c_str () );
267290 co_return false ;
268291 }
269292
270- if (tag.has_value ()) {
271- kphp::log::warning (" The authenticated tag cannot be provided for cipher that doesn not support AEAD" );
293+ bool aead{is_aead_algorithm (*algorithm)};
294+ if (aead && (!tag.has_value () || tag_length == 0 )) {
295+ kphp::log::warning (" A tag must be provided when using AEAD mode" );
296+ co_return false ;
297+ }
298+ if (tag.has_value () && !aead) {
299+ kphp::log::warning (" The authenticated tag cannot be provided for cipher that does not support AEAD" );
272300 }
273- if (!aad.empty ()) {
274- kphp::log::warning (" The additional authenticated data cannot be provided for cipher that doesn not support AEAD" );
301+ if (!aad.empty () && !aead ) {
302+ kphp::log::warning (" The additional authenticated data cannot be provided for cipher that does not support AEAD" );
275303 }
276304 if (source_iv.empty ()) {
277305 kphp::log::warning (" Using an empty Initialization Vector (iv) is potentially insecure and not recommended" );
278306 }
279307
280308 auto key_iv{algorithm_pad_key_iv (*algorithm, source_key, source_iv, options)};
281- if (key_iv.is_null ()) {
309+ if (! key_iv.has_value ()) {
282310 co_return false ;
283311 }
284312
285- tl::CbcEncrypt cbc_encrypt{.algorithm = *algorithm,
286- .padding = tl::BlockPadding::PKCS7,
287- .passphrase = {.value = {key_iv.val ().first .c_str (), key_iv.val ().first .size ()}},
288- .iv = {.value = {key_iv.val ().second .c_str (), key_iv.val ().second .size ()}},
289- .data = {.value = {data.c_str (), data.size ()}}};
290- tl::storer tls{cbc_encrypt.footprint ()};
291- cbc_encrypt.store (tls);
313+ tl::BlockPadding padding{tl::BlockPadding::PKCS7};
314+ if (options & static_cast <int64_t >(cipher_opts::OPENSSL_ZERO_PADDING)) {
315+ padding = tl::BlockPadding::NO_PADDING;
316+ }
317+ tl::Encrypt encrypt{.algorithm = *algorithm,
318+ .padding = padding,
319+ .passphrase = {.value = {key_iv.val ().first .c_str (), key_iv.val ().first .size ()}},
320+ .iv = {.value = {key_iv.val ().second .c_str (), key_iv.val ().second .size ()}},
321+ .tag_size = {.value = tag_length},
322+ .aad = {.value = {aad.c_str (), aad.size ()}},
323+ .data = {.value = {data.c_str (), data.size ()}}};
324+ tl::storer tls{encrypt.footprint ()};
325+ encrypt.store (tls);
292326
293327 auto expected_stream{kphp::component::stream::open (CRYPTO_COMPONENT_NAME, k2::stream_kind::component)};
294328 if (!expected_stream) [[unlikely]] {
@@ -302,9 +336,18 @@ kphp::coro::task<Optional<string>> f$openssl_encrypt(string data, string method,
302336 }
303337
304338 tl::fetcher tlf{response_bytes};
305- tl::String response{};
339+ tl::Maybe<tl::tuple<tl::string, 2 >> response{};
306340 kphp::log::assertion (response.fetch (tlf));
307- string result{response.inner .value .data (), static_cast <string::size_type>(response.inner .value .size ())};
341+ if (!response.opt_value ) {
342+ co_return false ;
343+ }
344+
345+ string result{(*response.opt_value ).value [0 ].value .data (), static_cast <string::size_type>((*response.opt_value ).value [0 ].value .size ())};
346+
347+ if (tag.has_value ()) {
348+ string received_tag{(*response.opt_value ).value [1 ].value .data (), static_cast <string::size_type>((*response.opt_value ).value [1 ].value .size ())};
349+ tag.value ().get () = std::move (received_tag);
350+ }
308351 co_return (options & static_cast <int64_t >(cipher_opts::OPENSSL_RAW_DATA)) ? std::move (result) : f$base64_encode (result);
309352}
310353
@@ -321,29 +364,36 @@ kphp::coro::task<Optional<string>> f$openssl_decrypt(string data, string method,
321364
322365 auto algorithm{parse_cipher_algorithm (method)};
323366 if (!algorithm.has_value ()) {
324- kphp::log::warning (" Unknown cipher algorithm" );
367+ kphp::log::warning (" Unknown cipher algorithm {} " , method. c_str () );
325368 co_return false ;
326369 }
327370
328- if (!tag.empty ()) {
329- kphp::log::warning (" The authenticated tag cannot be provided for cipher that doesn not support AEAD" );
371+ bool aead{is_aead_algorithm (*algorithm)};
372+ if (!tag.empty () && !aead) {
373+ kphp::log::warning (" The authenticated tag cannot be provided for cipher that does not support AEAD" );
330374 }
331- if (!aad.empty ()) {
332- kphp::log::warning (" The additional authenticated data cannot be provided for cipher that doesn not support AEAD" );
375+ if (!aad.empty () && !aead ) {
376+ kphp::log::warning (" The additional authenticated data cannot be provided for cipher that does not support AEAD" );
333377 }
334378
335379 auto key_iv{algorithm_pad_key_iv (*algorithm, source_key, source_iv, options)};
336- if (key_iv.is_null ()) {
380+ if (! key_iv.has_value ()) {
337381 co_return false ;
338382 }
339383
340- tl::CbcDecrypt cbc_decrypt{.algorithm = *algorithm,
341- .padding = tl::BlockPadding::PKCS7,
342- .passphrase = {.value = {key_iv.val ().first .c_str (), key_iv.val ().first .size ()}},
343- .iv = {.value = {key_iv.val ().second .c_str (), key_iv.val ().second .size ()}},
344- .data = {.value = {data.c_str (), data.size ()}}};
345- tl::storer tls{cbc_decrypt.footprint ()};
346- cbc_decrypt.store (tls);
384+ tl::BlockPadding padding{tl::BlockPadding::PKCS7};
385+ if (options & static_cast <int64_t >(cipher_opts::OPENSSL_ZERO_PADDING)) {
386+ padding = tl::BlockPadding::NO_PADDING;
387+ }
388+ tl::Decrypt decrypt{.algorithm = *algorithm,
389+ .padding = padding,
390+ .passphrase = {.value = {key_iv.val ().first .c_str (), key_iv.val ().first .size ()}},
391+ .iv = {.value = {key_iv.val ().second .c_str (), key_iv.val ().second .size ()}},
392+ .tag = {.value = {tag.c_str (), tag.size ()}},
393+ .aad = {.value = {aad.c_str (), aad.size ()}},
394+ .data = {.value = {data.c_str (), data.size ()}}};
395+ tl::storer tls{decrypt.footprint ()};
396+ decrypt.store (tls);
347397
348398 auto expected_stream{kphp::component::stream::open (CRYPTO_COMPONENT_NAME, k2::stream_kind::component)};
349399 if (!expected_stream) [[unlikely]] {
@@ -357,9 +407,13 @@ kphp::coro::task<Optional<string>> f$openssl_decrypt(string data, string method,
357407 }
358408
359409 tl::fetcher tlf{response_bytes};
360- tl::String response{};
410+ tl::Maybe<tl::string> response{};
361411 kphp::log::assertion (response.fetch (tlf));
362- co_return string{response.inner .value .data (), static_cast <string::size_type>(response.inner .value .size ())};
412+ if (!response.opt_value ) {
413+ co_return false ;
414+ }
415+
416+ co_return string{(*response.opt_value ).value .data (), static_cast <string::size_type>((*response.opt_value ).value .size ())};
363417}
364418
365419kphp::coro::task<Optional<string>> f$openssl_pkey_get_public(string key) noexcept {
@@ -437,9 +491,13 @@ kphp::coro::task<bool> f$openssl_public_encrypt(string data, string& encrypted_d
437491 }
438492
439493 tl::fetcher tlf{response_bytes};
440- tl::String response{};
494+ tl::Maybe<tl::string> response{};
441495 kphp::log::assertion (response.fetch (tlf));
442- encrypted_data = {response.inner .value .data (), static_cast <string::size_type>(response.inner .value .size ())};
496+ if (!response.opt_value ) {
497+ co_return false ;
498+ }
499+
500+ encrypted_data = {(*response.opt_value ).value .data (), static_cast <string::size_type>((*response.opt_value ).value .size ())};
443501 co_return true ;
444502}
445503
@@ -473,9 +531,13 @@ kphp::coro::task<bool> f$openssl_private_decrypt(string data, string& decrypted_
473531 }
474532
475533 tl::fetcher tlf{response_bytes};
476- tl::String response{};
534+ tl::Maybe<tl::string> response{};
477535 kphp::log::assertion (response.fetch (tlf));
478- decrypted_data = {response.inner .value .data (), static_cast <string::size_type>(response.inner .value .size ())};
536+ if (!response.opt_value ) {
537+ co_return false ;
538+ }
539+
540+ decrypted_data = {(*response.opt_value ).value .data (), static_cast <string::size_type>((*response.opt_value ).value .size ())};
479541 co_return true ;
480542}
481543
0 commit comments