38 #if CRYPTOPP_PATH_USES_PLUS_SIGN
39 #include <crypto++/aes.h>
40 #include <crypto++/filters.h>
41 #include <crypto++/modes.h>
42 #include <crypto++/sha.h>
44 #include <cryptopp/aes.h>
45 #include <cryptopp/filters.h>
46 #include <cryptopp/modes.h>
47 #include <cryptopp/sha.h>
48 #endif // CRYPTOPP_PATH_USES_PLUS_SIGN
49 #endif // HAS_CRYPTOPP
51 #include "codecs3/field_codec_var_bytes.h"
52 #include "codecs4/field_codec_hash.h"
53 #include "codecs4/field_codec_var_bytes.h"
54 #include "field_codec_id.h"
56 #include "codecs2/default_field_codec_impl.h"
57 #include "codecs3/default_field_codec_impl.h"
58 #include "codecs4/default_field_codec_impl.h"
60 #include "option_extensions.pb.h"
65 using namespace dccl::logger;
67 using google::protobuf::Descriptor;
68 using google::protobuf::FieldDescriptor;
69 using google::protobuf::Reflection;
76 : id_codec_(std::move(dccl_id_codec_name))
81 if (!library_path.empty())
89 for (
auto& dl_handle : dl_handles_)
91 unload_library(dl_handle);
96 void dccl::Codec::set_default_codecs()
98 using google::protobuf::FieldDescriptor;
134 const Descriptor* desc = msg.GetDescriptor();
136 dlog.
is(DEBUG1, ENCODE) && dlog <<
"Began encoding message of type: " << desc->full_name()
141 int32 dccl_id = id_internal(desc, user_id);
142 size_t head_byte_size = 0;
144 if (!msg.IsInitialized() && !header_only)
146 std::stringstream ss;
148 ss <<
"Message is not properly initialized. All `required` fields must be set. Fields "
150 << get_all_error_fields_in_message(msg);
155 if (!id2desc_.count(dccl_id))
156 throw(
Exception(
"Message id " + std::to_string(dccl_id) +
157 " has not been loaded. Call load() before encoding this type."),
160 std::shared_ptr<FieldCodecBase> codec = manager_.find(desc);
161 std::shared_ptr<internal::FromProtoCppTypeBase> helper = manager_.type_helper().find(desc);
166 if (!desc->options().GetExtension(dccl::msg).omit_id())
167 id_codec()->field_encode(&head_bits,
static_cast<uint32>(dccl_id),
nullptr);
170 manager_.codec_data().message_data_);
171 msg_stack.push(msg.GetDescriptor());
172 codec->base_encode(&head_bits, msg, HEAD, strict_);
175 head_byte_size = ceil_bits2bytes(head_bits.size());
176 head_bits.resize(head_byte_size * BITS_IN_BYTE);
180 dlog.
is(DEBUG2, ENCODE) &&
181 dlog <<
"as requested, skipping encoding and encrypting body." << std::endl;
185 codec->base_encode(&body_bits, msg, BODY, strict_);
190 throw(
Exception(
"Failed to find (dccl.msg).codec `" +
191 desc->options().GetExtension(dccl::msg).codec() +
"`"),
197 dlog.
is(DEBUG1, ENCODE) &&
198 dlog <<
"Message " << desc->full_name()
199 <<
" failed to encode because a field was out of bounds and strict == true: "
200 << e.what() << std::endl;
203 catch (std::exception& e)
205 std::stringstream ss;
207 ss <<
"Message " << desc->full_name() <<
" failed to encode. Reason: " << e.what();
209 dlog.
is(DEBUG1, ENCODE) && dlog << ss.str() << std::endl;
215 bool header_only ,
int user_id )
217 const Descriptor* desc = msg.GetDescriptor();
220 encode_internal(msg, header_only, head_bits, body_bits, user_id);
222 size_t head_byte_size = ceil_bits2bytes(head_bits.size());
223 if (max_len < head_byte_size)
225 throw std::length_error(
"max_len must be >= head_byte_size");
229 dlog.
is(DEBUG2, ENCODE) && dlog <<
"Head bytes (bits): " << head_byte_size <<
"("
230 << head_bits.size() <<
")" << std::endl;
231 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Head (bin): " << head_bits << std::endl;
232 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Head (hex): "
233 <<
hex_encode(bytes, bytes + head_byte_size) << std::endl;
235 size_t body_byte_size = 0;
238 body_byte_size = ceil_bits2bytes(body_bits.size());
239 if (max_len < (head_byte_size + body_byte_size))
241 throw std::length_error(
"max_len must be >= (head_byte_size + body_byte_size)");
243 body_bits.
to_byte_string(bytes + head_byte_size, max_len - head_byte_size);
245 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Body (bin): " << body_bits << std::endl;
246 dlog.
is(DEBUG3, ENCODE) &&
247 dlog <<
"Unencrypted Body (hex): "
248 <<
hex_encode(bytes + head_byte_size, bytes + head_byte_size + body_byte_size)
250 dlog.
is(DEBUG2, ENCODE) && dlog <<
"Body bytes (bits): " << body_byte_size <<
"("
251 << body_bits.size() <<
")" << std::endl;
253 int32 dccl_id = id_internal(desc, user_id);
254 if (!crypto_key_.empty() && !skip_crypto_ids_.count(dccl_id))
256 std::string head_bytes(bytes, bytes + head_byte_size);
257 std::string body_bytes(bytes + head_byte_size, bytes + head_byte_size + body_byte_size);
258 encrypt(&body_bytes, head_bytes);
259 std::memcpy(bytes + head_byte_size, body_bytes.data(), body_bytes.size());
262 dlog.
is(logger::DEBUG3, logger::ENCODE) &&
263 dlog <<
"Encrypted Body (hex): "
264 <<
hex_encode(bytes + head_byte_size, bytes + head_byte_size + body_byte_size)
268 dlog.
is(DEBUG1, ENCODE) && dlog <<
"Successfully encoded message of type: " << desc->full_name()
271 return head_byte_size + body_byte_size;
275 bool header_only ,
int user_id )
277 const Descriptor* desc = msg.GetDescriptor();
280 encode_internal(msg, header_only, head_bits, body_bits, user_id);
284 dlog.
is(DEBUG2, ENCODE) && dlog <<
"Head bytes (bits): " << head_bytes.size() <<
"("
285 << head_bits.size() <<
")" << std::endl;
286 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Head (bin): " << head_bits << std::endl;
287 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Head (hex): " <<
hex_encode(head_bytes)
290 std::string body_bytes;
295 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Body (bin): " << body_bits << std::endl;
296 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Body (hex): " <<
hex_encode(body_bytes)
298 dlog.
is(DEBUG2, ENCODE) && dlog <<
"Body bytes (bits): " << body_bytes.size() <<
"("
299 << body_bits.size() <<
")" << std::endl;
301 int32 dccl_id = id_internal(desc, user_id);
302 if (!crypto_key_.empty() && !skip_crypto_ids_.count(dccl_id))
303 encrypt(&body_bytes, head_bytes);
305 dlog.
is(logger::DEBUG3, logger::ENCODE) &&
306 dlog <<
"Encrypted Body (hex): " <<
hex_encode(body_bytes) << std::endl;
309 dlog.
is(DEBUG1, ENCODE) && dlog <<
"Successfully encoded message of type: " << desc->full_name()
311 *bytes += head_bytes + body_bytes;
322 const auto& msg_opt = desc->options().GetExtension(dccl::msg);
323 if (user_id < 0 && !msg_opt.has_id() && !msg_opt.omit_id())
325 Exception(
"Missing message option `(dccl.msg).id`. Specify a unique id (e.g. 3) in "
326 "the body of your .proto message using \"option (dccl.msg).id = 3\"",
328 if (!msg_opt.has_max_bytes())
329 throw(
Exception(
"Missing message option `(dccl.msg).max_bytes`. Specify a maximum "
330 "(encoded) message size in bytes (e.g. 32) in the body of your .proto "
331 "message using \"option (dccl.msg).max_bytes = 32\"",
334 if (!msg_opt.has_codec_version())
336 "No (dccl.msg).codec_version set for DCCL Message '" + desc->full_name() +
337 "'. For new messages, set 'option (dccl.msg).codec_version = 4' in the "
338 "message definition for " +
339 desc->full_name() +
" to use the default DCCL4 codecs.",
342 std::shared_ptr<FieldCodecBase> codec = manager_.find(desc);
344 int32 dccl_id = id_internal(desc, user_id);
346 unsigned head_size_bits, body_size_bits;
347 codec->base_max_size(&head_size_bits, desc, HEAD);
348 codec->base_max_size(&body_size_bits, desc, BODY);
350 unsigned id_bits = 0;
351 if (!msg_opt.omit_id())
352 id_codec()->field_size(&id_bits,
static_cast<uint32>(dccl_id),
nullptr);
353 head_size_bits += id_bits;
355 const unsigned byte_size =
356 ceil_bits2bytes(head_size_bits) + ceil_bits2bytes(body_size_bits);
358 auto actual_max = byte_size;
359 auto allowed_max = msg_opt.max_bytes();
360 if (actual_max > allowed_max)
361 throw(
Exception(
"Actual maximum size of message (" + std::to_string(actual_max) +
362 "B) exceeds allowed maximum (" + std::to_string(allowed_max) +
364 "bounds, remove fields, improve codecs, or increase the allowed "
365 "value in (dccl.msg).max_bytes",
368 codec->base_validate(desc, HEAD);
369 codec->base_validate(desc, BODY);
371 if (id2desc_.count(dccl_id) && desc != id2desc_.find(dccl_id)->second)
373 std::stringstream ss;
374 ss <<
"`dccl id` " << dccl_id <<
" is already in use by Message "
375 << id2desc_.find(dccl_id)->second->full_name() <<
": "
376 << id2desc_.find(dccl_id)->second;
382 id2desc_.insert(std::make_pair(dccl_id, desc));
385 dlog.
is(DEBUG1) && dlog <<
"Successfully validated message of type: " << desc->full_name()
388 std::size_t hash_value = 0;
389 codec->base_hash(&hash_value, desc, HEAD);
390 codec->base_hash(&hash_value, desc, BODY);
391 manager_.set_hash(desc, hash_value);
404 dlog.
is(DEBUG1) && dlog <<
"Message " << desc->full_name() <<
": " << desc
405 <<
" failed validation. Reason: " << e.what() <<
"\n"
406 <<
"If possible, information about the Message are printed above. "
415 unsigned int erased = 0;
416 for (
auto it = id2desc_.begin(); it != id2desc_.end();)
418 if (it->second == desc)
421 id2desc_.erase(it++);
430 dlog.
is(DEBUG1) && dlog <<
"Message " << desc->full_name()
431 <<
": is not loaded. Ignoring unload request." << std::endl;
437 if (id2desc_.count(dccl_id))
439 id2desc_.erase(dccl_id);
443 dlog.
is(DEBUG1) && dlog <<
"Message with id " << dccl_id
444 <<
": is not loaded. Ignoring unload request." << std::endl;
451 const Descriptor* desc = msg.GetDescriptor();
453 std::shared_ptr<FieldCodecBase> codec = manager_.find(desc);
455 int32 dccl_id = id_internal(desc, user_id);
456 unsigned head_size_bits;
457 codec->base_size(&head_size_bits, msg, HEAD);
459 unsigned id_bits = 0;
460 if (!desc->options().GetExtension(dccl::msg).omit_id())
461 id_codec()->field_size(&id_bits,
static_cast<uint32>(dccl_id),
nullptr);
462 head_size_bits += id_bits;
464 unsigned body_size_bits;
465 codec->base_size(&body_size_bits, msg, BODY);
467 const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
468 const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
469 return head_size_bytes + body_size_bytes;
474 std::shared_ptr<FieldCodecBase> codec = manager_.find(desc);
476 unsigned head_size_bits;
477 codec->base_max_size(&head_size_bits, desc, HEAD);
479 unsigned id_bits = 0;
480 if (!desc->options().GetExtension(dccl::msg).omit_id())
481 id_codec()->field_max_size(&id_bits,
nullptr);
482 head_size_bits += id_bits;
484 unsigned body_size_bits;
485 codec->base_max_size(&body_size_bits, desc, BODY);
487 const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
488 const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
489 return head_size_bytes + body_size_bytes;
494 std::shared_ptr<FieldCodecBase> codec = manager_.find(desc);
496 unsigned head_size_bits;
497 codec->base_min_size(&head_size_bits, desc, HEAD);
499 unsigned id_bits = 0;
500 if (!desc->options().GetExtension(dccl::msg).omit_id())
501 id_codec()->field_min_size(&id_bits,
nullptr);
502 head_size_bits += id_bits;
504 unsigned body_size_bits;
505 codec->base_min_size(&body_size_bits, desc, BODY);
507 const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
508 const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
509 return head_size_bytes + body_size_bytes;
515 bool is_dlog =
false;
516 if (!param_os || param_os == &dlog)
518 std::stringstream ss;
519 std::ostream* os = (is_dlog) ? &ss : param_os;
521 if (!is_dlog || dlog.
check(INFO))
525 bool omit_id = desc->options().GetExtension(dccl::msg).omit_id();
527 std::shared_ptr<FieldCodecBase> codec = manager_.find(desc);
529 unsigned config_head_bit_size, body_bit_size;
530 codec->base_max_size(&config_head_bit_size, desc, HEAD);
531 codec->base_max_size(&body_bit_size, desc, BODY);
533 int32 dccl_id = id_internal_const(desc, user_id);
534 unsigned id_bit_size = 0;
536 id_codec()->field_size(&id_bit_size,
static_cast<uint32>(dccl_id),
nullptr);
538 const unsigned bit_size = id_bit_size + config_head_bit_size + body_bit_size;
540 const unsigned byte_size = ceil_bits2bytes(config_head_bit_size + id_bit_size) +
541 ceil_bits2bytes(body_bit_size);
543 const unsigned allowed_byte_size = desc->options().GetExtension(dccl::msg).max_bytes();
544 const unsigned allowed_bit_size = allowed_byte_size * BITS_IN_BYTE;
547 if (manager_.has_hash(desc))
548 hash = hash_as_string(manager_.hash(desc));
550 std::string message_name;
552 message_name += std::to_string(dccl_id) +
": ";
553 message_name += desc->full_name() +
" {" + hash +
"}";
554 std::string guard = build_guard_for_console_output(message_name,
'=');
555 std::string bits_dccl_head_str =
"dccl.id head";
556 std::string bits_user_head_str =
"user head";
557 std::string bits_body_str =
"body";
558 std::string bits_padding_str =
"padding to full byte";
560 const int bits_width = 40;
561 const int spaces = 8;
562 std::string indent = std::string(spaces,
' ');
564 *os << guard <<
" " << message_name <<
" " << guard <<
"\n"
565 <<
"Actual maximum size of message: " << byte_size <<
" bytes / "
566 << byte_size * BITS_IN_BYTE <<
" bits\n"
567 << indent << bits_dccl_head_str << std::setfill(
'.')
568 << std::setw(bits_width - bits_dccl_head_str.size()) << id_bit_size <<
"\n"
569 << indent << bits_user_head_str << std::setfill(
'.')
570 << std::setw(bits_width - bits_user_head_str.size()) << config_head_bit_size <<
"\n"
571 << indent << bits_body_str << std::setfill(
'.')
572 << std::setw(bits_width - bits_body_str.size()) << body_bit_size <<
"\n"
573 << indent << bits_padding_str << std::setfill(
'.')
574 << std::setw(bits_width - bits_padding_str.size())
575 << byte_size * BITS_IN_BYTE - bit_size <<
"\n"
576 <<
"Allowed maximum size of message: " << allowed_byte_size <<
" bytes / "
577 << allowed_bit_size <<
" bits\n";
579 std::string header_str =
"Header";
580 std::string header_guard = build_guard_for_console_output(header_str,
'-');
582 *os << header_guard <<
" " << header_str <<
" " << header_guard <<
"\n";
583 *os << bits_dccl_head_str << std::setfill(
'.')
584 << std::setw(bits_width - bits_dccl_head_str.size() + spaces) << id_bit_size <<
" {"
585 << (omit_id ? std::string(
"omit_id: true") : id_codec()->name()) <<
"}\n";
586 codec->base_info(os, desc, HEAD);
589 std::string body_str =
"Body";
590 std::string body_guard = build_guard_for_console_output(body_str,
'-');
592 *os << body_guard <<
" " << body_str <<
" " << body_guard <<
"\n";
593 codec->base_info(os, desc, BODY);
600 dlog.
is(INFO) && dlog << ss.str() << std::endl;
605 dlog <<
"Message " << desc->full_name()
606 <<
" cannot provide information due to invalid configuration. Reason: "
607 << e.what() << std::endl;
612 void dccl::Codec::encrypt(std::string* s,
const std::string& nonce )
614 #if DCCL_HAS_CRYPTOPP
615 using namespace CryptoPP;
619 StringSource unused(nonce,
true,
new HashFilter(hash,
new StringSink(iv)));
621 CTR_Mode<AES>::Encryption encryptor;
622 encryptor.SetKeyWithIV((
byte*)crypto_key_.c_str(), crypto_key_.size(), (
byte*)iv.c_str());
625 StreamTransformationFilter in(encryptor,
new StringSink(cipher));
626 in.Put((
byte*)s->c_str(), s->size());
632 void dccl::Codec::decrypt(std::string* s,
const std::string& nonce)
634 #if DCCL_HAS_CRYPTOPP
635 using namespace CryptoPP;
639 StringSource unused(nonce,
true,
new HashFilter(hash,
new StringSink(iv)));
641 CTR_Mode<AES>::Decryption decryptor;
642 decryptor.SetKeyWithIV((
byte*)crypto_key_.c_str(), crypto_key_.size(), (
byte*)iv.c_str());
644 std::string recovered;
646 StreamTransformationFilter out(decryptor,
new StringSink(recovered));
647 out.Put((
byte*)s->c_str(), s->size());
655 void* handle = dlopen(library_path.c_str(), RTLD_LAZY);
657 dl_handles_.push_back(handle);
658 load_library(handle);
664 throw(
Exception(
"Null shared library handle passed to load_library"));
668 dccl_load_ptr = (void (*)(
dccl::Codec*))dlsym(dl_handle,
"dccl3_load");
670 (*dccl_load_ptr)(
this);
676 throw(
Exception(
"Null shared library handle passed to unload_library"));
680 dccl_unload_ptr = (void (*)(
dccl::Codec*))dlsym(dl_handle,
"dccl3_unload");
682 (*dccl_unload_ptr)(
this);
686 const std::string& passphrase,
687 const std::set<int32>& do_not_encrypt_ids )
689 if (!crypto_key_.empty())
691 skip_crypto_ids_.clear();
693 #if DCCL_HAS_CRYPTOPP
694 using namespace CryptoPP;
697 StringSource unused(passphrase,
true,
new HashFilter(hash,
new StringSink(crypto_key_)));
699 dlog.
is(DEBUG1) && dlog <<
"Cryptography enabled with given passphrase" << std::endl;
701 dlog.
is(DEBUG1) && dlog <<
"Cryptography disabled because DCCL was compiled without support of "
702 "Crypto++. Install Crypto++ and recompile to enable cryptography."
706 skip_crypto_ids_ = do_not_encrypt_ids;
711 bool is_dlog =
false;
712 if (!param_os || param_os == &dlog)
714 std::stringstream ss;
715 std::ostream* os = (is_dlog) ? &ss : param_os;
716 if (!is_dlog || dlog.
check(INFO))
718 std::string codec_str =
"Dynamic Compact Control Language (DCCL) Codec";
719 std::string codec_guard = build_guard_for_console_output(codec_str,
'|');
721 *os << codec_guard <<
" " << codec_str <<
" " << codec_guard <<
"\n";
722 *os << id2desc_.size() <<
" messages loaded.\n";
723 *os <<
"Field sizes are in bits unless otherwise noted."
726 for (
auto it : id2desc_) info(it.second, os, it.first);
730 dlog.
is(INFO) && dlog << ss.str() << std::endl;
739 id_codec_ = id_codec_name;
744 std::string dccl::Codec::build_guard_for_console_output(std::string& base,
char guard_char)
const
747 return (base.size() < console_width_)
748 ? std::string((console_width_ - base.size()) / 2, guard_char)
760 std::stringstream output_stream;
762 const google::protobuf::Descriptor* descriptor = message.GetDescriptor();
763 const google::protobuf::Reflection* reflection = message.GetReflection();
764 const uint8_t depth_spacing = 4;
767 const int32_t field_count = descriptor->field_count();
769 for (int32_t index = 0; index < field_count; ++index)
771 if (descriptor->field(index)->is_required())
773 if (!reflection->HasField(message, descriptor->field(index)))
775 output_stream << std::string(depth * depth_spacing,
' ')
776 << descriptor->field(index)->number() <<
": "
777 << descriptor->field(index)->name() <<
"\n";
783 std::vector<const google::protobuf::FieldDescriptor*> fields;
784 reflection->ListFields(message, &fields);
786 for (
const google::protobuf::FieldDescriptor* field : fields)
788 if (field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE)
790 output_stream << std::string(depth * depth_spacing,
' ') << field->number() <<
": "
791 << field->name() <<
"\n";
793 if (field->is_repeated())
795 int32_t size = reflection->FieldSize(message, field);
797 for (int32_t index = 0; index < size; ++index)
800 reflection->GetRepeatedMessage(message, field, index);
802 output_stream << std::string((depth + 1) * depth_spacing,
' ') <<
"[" << index
806 output_stream << get_all_error_fields_in_message(sub_message, depth + 2);
812 reflection->GetMessage(message, field);
813 output_stream << get_all_error_fields_in_message(sub_message, depth + 1);
818 return output_stream.str();