36 #if CRYPTOPP_PATH_USES_PLUS_SIGN
37 #include <crypto++/aes.h>
38 #include <crypto++/filters.h>
39 #include <crypto++/modes.h>
40 #include <crypto++/sha.h>
42 #include <cryptopp/aes.h>
43 #include <cryptopp/filters.h>
44 #include <cryptopp/modes.h>
45 #include <cryptopp/sha.h>
46 #endif // CRYPTOPP_PATH_USES_PLUS_SIGN
47 #endif // HAS_CRYPTOPP
49 #include "codecs3/field_codec_var_bytes.h"
50 #include "codecs4/field_codec_hash.h"
51 #include "codecs4/field_codec_var_bytes.h"
52 #include "field_codec_id.h"
54 #include "codecs2/default_field_codec_impl.h"
55 #include "codecs3/default_field_codec_impl.h"
56 #include "codecs4/default_field_codec_impl.h"
58 #include "option_extensions.pb.h"
63 using namespace dccl::logger;
65 using google::protobuf::Descriptor;
66 using google::protobuf::FieldDescriptor;
67 using google::protobuf::Reflection;
74 : id_codec_(std::move(dccl_id_codec_name))
79 if (!library_path.empty())
87 for (
auto& dl_handle : dl_handles_)
89 unload_library(dl_handle);
94 void dccl::Codec::set_default_codecs()
96 using google::protobuf::FieldDescriptor;
132 const Descriptor* desc = msg.GetDescriptor();
134 dlog.
is(DEBUG1, ENCODE) && dlog <<
"Began encoding message of type: " << desc->full_name()
139 int32 dccl_id = id_internal(desc, user_id);
140 size_t head_byte_size = 0;
142 if (!msg.IsInitialized() && !header_only)
144 std::stringstream ss;
146 ss <<
"Message is not properly initialized. All `required` fields must be set. Fields "
148 << get_all_error_fields_in_message(msg);
153 if (!id2desc_.count(dccl_id))
154 throw(
Exception(
"Message id " + std::to_string(dccl_id) +
155 " has not been loaded. Call load() before encoding this type."),
158 std::shared_ptr<FieldCodecBase> codec = manager_.find(desc);
159 std::shared_ptr<internal::FromProtoCppTypeBase> helper = manager_.type_helper().find(desc);
164 if (!desc->options().GetExtension(dccl::msg).omit_id())
165 id_codec()->field_encode(&head_bits,
static_cast<uint32>(dccl_id),
nullptr);
168 manager_.codec_data().message_data_);
169 msg_stack.push(msg.GetDescriptor());
170 codec->base_encode(&head_bits, msg, HEAD, strict_);
173 head_byte_size = ceil_bits2bytes(head_bits.size());
174 head_bits.resize(head_byte_size * BITS_IN_BYTE);
178 dlog.
is(DEBUG2, ENCODE) &&
179 dlog <<
"as requested, skipping encoding and encrypting body." << std::endl;
183 codec->base_encode(&body_bits, msg, BODY, strict_);
188 throw(
Exception(
"Failed to find (dccl.msg).codec `" +
189 desc->options().GetExtension(dccl::msg).codec() +
"`"),
195 dlog.
is(DEBUG1, ENCODE) &&
196 dlog <<
"Message " << desc->full_name()
197 <<
" failed to encode because a field was out of bounds and strict == true: "
198 << e.what() << std::endl;
201 catch (std::exception& e)
203 std::stringstream ss;
205 ss <<
"Message " << desc->full_name() <<
" failed to encode. Reason: " << e.what();
207 dlog.
is(DEBUG1, ENCODE) && dlog << ss.str() << std::endl;
213 bool header_only ,
int user_id )
215 const Descriptor* desc = msg.GetDescriptor();
218 encode_internal(msg, header_only, head_bits, body_bits, user_id);
220 size_t head_byte_size = ceil_bits2bytes(head_bits.size());
221 if (max_len < head_byte_size)
223 throw std::length_error(
"max_len must be >= head_byte_size");
227 dlog.
is(DEBUG2, ENCODE) && dlog <<
"Head bytes (bits): " << head_byte_size <<
"("
228 << head_bits.size() <<
")" << std::endl;
229 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Head (bin): " << head_bits << std::endl;
230 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Head (hex): "
231 <<
hex_encode(bytes, bytes + head_byte_size) << std::endl;
233 size_t body_byte_size = 0;
236 body_byte_size = ceil_bits2bytes(body_bits.size());
237 if (max_len < (head_byte_size + body_byte_size))
239 throw std::length_error(
"max_len must be >= (head_byte_size + body_byte_size)");
241 body_bits.
to_byte_string(bytes + head_byte_size, max_len - head_byte_size);
243 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Body (bin): " << body_bits << std::endl;
244 dlog.
is(DEBUG3, ENCODE) &&
245 dlog <<
"Unencrypted Body (hex): "
246 <<
hex_encode(bytes + head_byte_size, bytes + head_byte_size + body_byte_size)
248 dlog.
is(DEBUG2, ENCODE) && dlog <<
"Body bytes (bits): " << body_byte_size <<
"("
249 << body_bits.size() <<
")" << std::endl;
251 int32 dccl_id = id_internal(desc, user_id);
252 if (!crypto_key_.empty() && !skip_crypto_ids_.count(dccl_id))
254 std::string head_bytes(bytes, bytes + head_byte_size);
255 std::string body_bytes(bytes + head_byte_size, bytes + head_byte_size + body_byte_size);
256 encrypt(&body_bytes, head_bytes);
257 std::memcpy(bytes + head_byte_size, body_bytes.data(), body_bytes.size());
260 dlog.
is(logger::DEBUG3, logger::ENCODE) &&
261 dlog <<
"Encrypted Body (hex): "
262 <<
hex_encode(bytes + head_byte_size, bytes + head_byte_size + body_byte_size)
266 dlog.
is(DEBUG1, ENCODE) && dlog <<
"Successfully encoded message of type: " << desc->full_name()
269 return head_byte_size + body_byte_size;
273 bool header_only ,
int user_id )
275 const Descriptor* desc = msg.GetDescriptor();
278 encode_internal(msg, header_only, head_bits, body_bits, user_id);
282 dlog.
is(DEBUG2, ENCODE) && dlog <<
"Head bytes (bits): " << head_bytes.size() <<
"("
283 << head_bits.size() <<
")" << std::endl;
284 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Head (bin): " << head_bits << std::endl;
285 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Head (hex): " <<
hex_encode(head_bytes)
288 std::string body_bytes;
293 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Body (bin): " << body_bits << std::endl;
294 dlog.
is(DEBUG3, ENCODE) && dlog <<
"Unencrypted Body (hex): " <<
hex_encode(body_bytes)
296 dlog.
is(DEBUG2, ENCODE) && dlog <<
"Body bytes (bits): " << body_bytes.size() <<
"("
297 << body_bits.size() <<
")" << std::endl;
299 int32 dccl_id = id_internal(desc, user_id);
300 if (!crypto_key_.empty() && !skip_crypto_ids_.count(dccl_id))
301 encrypt(&body_bytes, head_bytes);
303 dlog.
is(logger::DEBUG3, logger::ENCODE) &&
304 dlog <<
"Encrypted Body (hex): " <<
hex_encode(body_bytes) << std::endl;
307 dlog.
is(DEBUG1, ENCODE) && dlog <<
"Successfully encoded message of type: " << desc->full_name()
309 *bytes += head_bytes + body_bytes;
320 const auto& msg_opt = desc->options().GetExtension(dccl::msg);
321 if (user_id < 0 && !msg_opt.has_id() && !msg_opt.omit_id())
323 Exception(
"Missing message option `(dccl.msg).id`. Specify a unique id (e.g. 3) in "
324 "the body of your .proto message using \"option (dccl.msg).id = 3\"",
326 if (!msg_opt.has_max_bytes())
327 throw(
Exception(
"Missing message option `(dccl.msg).max_bytes`. Specify a maximum "
328 "(encoded) message size in bytes (e.g. 32) in the body of your .proto "
329 "message using \"option (dccl.msg).max_bytes = 32\"",
332 if (!msg_opt.has_codec_version())
334 "No (dccl.msg).codec_version set for DCCL Message '" + desc->full_name() +
335 "'. For new messages, set 'option (dccl.msg).codec_version = 4' in the "
336 "message definition for " +
337 desc->full_name() +
" to use the default DCCL4 codecs.",
340 std::shared_ptr<FieldCodecBase> codec = manager_.find(desc);
342 int32 dccl_id = id_internal(desc, user_id);
344 unsigned head_size_bits, body_size_bits;
345 codec->base_max_size(&head_size_bits, desc, HEAD);
346 codec->base_max_size(&body_size_bits, desc, BODY);
348 unsigned id_bits = 0;
349 if (!msg_opt.omit_id())
350 id_codec()->field_size(&id_bits,
static_cast<uint32>(dccl_id),
nullptr);
351 head_size_bits += id_bits;
353 const unsigned byte_size =
354 ceil_bits2bytes(head_size_bits) + ceil_bits2bytes(body_size_bits);
356 auto actual_max = byte_size;
357 auto allowed_max = msg_opt.max_bytes();
358 if (actual_max > allowed_max)
359 throw(
Exception(
"Actual maximum size of message (" + std::to_string(actual_max) +
360 "B) exceeds allowed maximum (" + std::to_string(allowed_max) +
362 "bounds, remove fields, improve codecs, or increase the allowed "
363 "value in (dccl.msg).max_bytes",
366 codec->base_validate(desc, HEAD);
367 codec->base_validate(desc, BODY);
369 if (id2desc_.count(dccl_id) && desc != id2desc_.find(dccl_id)->second)
371 std::stringstream ss;
372 ss <<
"`dccl id` " << dccl_id <<
" is already in use by Message "
373 << id2desc_.find(dccl_id)->second->full_name() <<
": "
374 << id2desc_.find(dccl_id)->second;
380 id2desc_.insert(std::make_pair(dccl_id, desc));
383 dlog.
is(DEBUG1) && dlog <<
"Successfully validated message of type: " << desc->full_name()
386 std::size_t hash_value = 0;
387 codec->base_hash(&hash_value, desc, HEAD);
388 codec->base_hash(&hash_value, desc, BODY);
389 manager_.set_hash(desc, hash_value);
402 dlog.
is(DEBUG1) && dlog <<
"Message " << desc->full_name() <<
": " << desc
403 <<
" failed validation. Reason: " << e.what() <<
"\n"
404 <<
"If possible, information about the Message are printed above. "
413 unsigned int erased = 0;
414 for (
auto it = id2desc_.begin(); it != id2desc_.end();)
416 if (it->second == desc)
419 id2desc_.erase(it++);
428 dlog.
is(DEBUG1) && dlog <<
"Message " << desc->full_name()
429 <<
": is not loaded. Ignoring unload request." << std::endl;
435 if (id2desc_.count(dccl_id))
437 id2desc_.erase(dccl_id);
441 dlog.
is(DEBUG1) && dlog <<
"Message with id " << dccl_id
442 <<
": is not loaded. Ignoring unload request." << std::endl;
449 const Descriptor* desc = msg.GetDescriptor();
451 std::shared_ptr<FieldCodecBase> codec = manager_.find(desc);
453 int32 dccl_id = id_internal(desc, user_id);
454 unsigned head_size_bits;
455 codec->base_size(&head_size_bits, msg, HEAD);
457 unsigned id_bits = 0;
458 if (!desc->options().GetExtension(dccl::msg).omit_id())
459 id_codec()->field_size(&id_bits,
static_cast<uint32>(dccl_id),
nullptr);
460 head_size_bits += id_bits;
462 unsigned body_size_bits;
463 codec->base_size(&body_size_bits, msg, BODY);
465 const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
466 const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
467 return head_size_bytes + body_size_bytes;
472 std::shared_ptr<FieldCodecBase> codec = manager_.find(desc);
474 unsigned head_size_bits;
475 codec->base_max_size(&head_size_bits, desc, HEAD);
477 unsigned id_bits = 0;
478 if (!desc->options().GetExtension(dccl::msg).omit_id())
479 id_codec()->field_max_size(&id_bits,
nullptr);
480 head_size_bits += id_bits;
482 unsigned body_size_bits;
483 codec->base_max_size(&body_size_bits, desc, BODY);
485 const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
486 const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
487 return head_size_bytes + body_size_bytes;
492 std::shared_ptr<FieldCodecBase> codec = manager_.find(desc);
494 unsigned head_size_bits;
495 codec->base_min_size(&head_size_bits, desc, HEAD);
497 unsigned id_bits = 0;
498 if (!desc->options().GetExtension(dccl::msg).omit_id())
499 id_codec()->field_min_size(&id_bits,
nullptr);
500 head_size_bits += id_bits;
502 unsigned body_size_bits;
503 codec->base_min_size(&body_size_bits, desc, BODY);
505 const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
506 const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
507 return head_size_bytes + body_size_bytes;
513 bool is_dlog =
false;
514 if (!param_os || param_os == &dlog)
516 std::stringstream ss;
517 std::ostream* os = (is_dlog) ? &ss : param_os;
519 if (!is_dlog || dlog.
check(INFO))
523 bool omit_id = desc->options().GetExtension(dccl::msg).omit_id();
525 std::shared_ptr<FieldCodecBase> codec = manager_.find(desc);
527 unsigned config_head_bit_size, body_bit_size;
528 codec->base_max_size(&config_head_bit_size, desc, HEAD);
529 codec->base_max_size(&body_bit_size, desc, BODY);
531 int32 dccl_id = id_internal_const(desc, user_id);
532 unsigned id_bit_size = 0;
534 id_codec()->field_size(&id_bit_size,
static_cast<uint32>(dccl_id),
nullptr);
536 const unsigned bit_size = id_bit_size + config_head_bit_size + body_bit_size;
538 const unsigned byte_size = ceil_bits2bytes(config_head_bit_size + id_bit_size) +
539 ceil_bits2bytes(body_bit_size);
541 const unsigned allowed_byte_size = desc->options().GetExtension(dccl::msg).max_bytes();
542 const unsigned allowed_bit_size = allowed_byte_size * BITS_IN_BYTE;
545 if (manager_.has_hash(desc))
546 hash = hash_as_string(manager_.hash(desc));
548 std::string message_name;
550 message_name += std::to_string(dccl_id) +
": ";
551 message_name += desc->full_name() +
" {" + hash +
"}";
552 std::string guard = build_guard_for_console_output(message_name,
'=');
553 std::string bits_dccl_head_str =
"dccl.id head";
554 std::string bits_user_head_str =
"user head";
555 std::string bits_body_str =
"body";
556 std::string bits_padding_str =
"padding to full byte";
558 const int bits_width = 40;
559 const int spaces = 8;
560 std::string indent = std::string(spaces,
' ');
562 *os << guard <<
" " << message_name <<
" " << guard <<
"\n"
563 <<
"Actual maximum size of message: " << byte_size <<
" bytes / "
564 << byte_size * BITS_IN_BYTE <<
" bits\n"
565 << indent << bits_dccl_head_str << std::setfill(
'.')
566 << std::setw(bits_width - bits_dccl_head_str.size()) << id_bit_size <<
"\n"
567 << indent << bits_user_head_str << std::setfill(
'.')
568 << std::setw(bits_width - bits_user_head_str.size()) << config_head_bit_size <<
"\n"
569 << indent << bits_body_str << std::setfill(
'.')
570 << std::setw(bits_width - bits_body_str.size()) << body_bit_size <<
"\n"
571 << indent << bits_padding_str << std::setfill(
'.')
572 << std::setw(bits_width - bits_padding_str.size())
573 << byte_size * BITS_IN_BYTE - bit_size <<
"\n"
574 <<
"Allowed maximum size of message: " << allowed_byte_size <<
" bytes / "
575 << allowed_bit_size <<
" bits\n";
577 std::string header_str =
"Header";
578 std::string header_guard = build_guard_for_console_output(header_str,
'-');
580 *os << header_guard <<
" " << header_str <<
" " << header_guard <<
"\n";
581 *os << bits_dccl_head_str << std::setfill(
'.')
582 << std::setw(bits_width - bits_dccl_head_str.size() + spaces) << id_bit_size <<
" {"
583 << (omit_id ? std::string(
"omit_id: true") : id_codec()->name()) <<
"}\n";
584 codec->base_info(os, desc, HEAD);
587 std::string body_str =
"Body";
588 std::string body_guard = build_guard_for_console_output(body_str,
'-');
590 *os << body_guard <<
" " << body_str <<
" " << body_guard <<
"\n";
591 codec->base_info(os, desc, BODY);
598 dlog.
is(INFO) && dlog << ss.str() << std::endl;
603 dlog <<
"Message " << desc->full_name()
604 <<
" cannot provide information due to invalid configuration. Reason: "
605 << e.what() << std::endl;
610 void dccl::Codec::encrypt(std::string* s,
const std::string& nonce )
612 #if DCCL_HAS_CRYPTOPP
613 using namespace CryptoPP;
617 StringSource unused(nonce,
true,
new HashFilter(hash,
new StringSink(iv)));
619 CTR_Mode<AES>::Encryption encryptor;
620 encryptor.SetKeyWithIV((
byte*)crypto_key_.c_str(), crypto_key_.size(), (
byte*)iv.c_str());
623 StreamTransformationFilter in(encryptor,
new StringSink(cipher));
624 in.Put((
byte*)s->c_str(), s->size());
630 void dccl::Codec::decrypt(std::string* s,
const std::string& nonce)
632 #if DCCL_HAS_CRYPTOPP
633 using namespace CryptoPP;
637 StringSource unused(nonce,
true,
new HashFilter(hash,
new StringSink(iv)));
639 CTR_Mode<AES>::Decryption decryptor;
640 decryptor.SetKeyWithIV((
byte*)crypto_key_.c_str(), crypto_key_.size(), (
byte*)iv.c_str());
642 std::string recovered;
644 StreamTransformationFilter out(decryptor,
new StringSink(recovered));
645 out.Put((
byte*)s->c_str(), s->size());
653 void* handle = dlopen(library_path.c_str(), RTLD_LAZY);
655 dl_handles_.push_back(handle);
656 load_library(handle);
662 throw(
Exception(
"Null shared library handle passed to load_library"));
666 dccl_load_ptr = (void (*)(
dccl::Codec*))dlsym(dl_handle,
"dccl3_load");
668 (*dccl_load_ptr)(
this);
674 throw(
Exception(
"Null shared library handle passed to unload_library"));
678 dccl_unload_ptr = (void (*)(
dccl::Codec*))dlsym(dl_handle,
"dccl3_unload");
680 (*dccl_unload_ptr)(
this);
684 const std::string& passphrase,
685 const std::set<int32>& do_not_encrypt_ids )
687 if (!crypto_key_.empty())
689 skip_crypto_ids_.clear();
691 #if DCCL_HAS_CRYPTOPP
692 using namespace CryptoPP;
695 StringSource unused(passphrase,
true,
new HashFilter(hash,
new StringSink(crypto_key_)));
697 dlog.
is(DEBUG1) && dlog <<
"Cryptography enabled with given passphrase" << std::endl;
699 dlog.
is(DEBUG1) && dlog <<
"Cryptography disabled because DCCL was compiled without support of "
700 "Crypto++. Install Crypto++ and recompile to enable cryptography."
704 skip_crypto_ids_ = do_not_encrypt_ids;
709 bool is_dlog =
false;
710 if (!param_os || param_os == &dlog)
712 std::stringstream ss;
713 std::ostream* os = (is_dlog) ? &ss : param_os;
714 if (!is_dlog || dlog.
check(INFO))
716 std::string codec_str =
"Dynamic Compact Control Language (DCCL) Codec";
717 std::string codec_guard = build_guard_for_console_output(codec_str,
'|');
719 *os << codec_guard <<
" " << codec_str <<
" " << codec_guard <<
"\n";
720 *os << id2desc_.size() <<
" messages loaded.\n";
721 *os <<
"Field sizes are in bits unless otherwise noted."
724 for (
auto it : id2desc_) info(it.second, os, it.first);
728 dlog.
is(INFO) && dlog << ss.str() << std::endl;
737 id_codec_ = id_codec_name;
742 std::string dccl::Codec::build_guard_for_console_output(std::string& base,
char guard_char)
const
745 return (base.size() < console_width_)
746 ? std::string((console_width_ - base.size()) / 2, guard_char)
758 std::stringstream output_stream;
760 const google::protobuf::Descriptor* descriptor = message.GetDescriptor();
761 const google::protobuf::Reflection* reflection = message.GetReflection();
762 const uint8_t depth_spacing = 4;
765 const int32_t field_count = descriptor->field_count();
767 for (int32_t index = 0; index < field_count; ++index)
769 if (descriptor->field(index)->is_required())
771 if (!reflection->HasField(message, descriptor->field(index)))
773 output_stream << std::string(depth * depth_spacing,
' ')
774 << descriptor->field(index)->number() <<
": "
775 << descriptor->field(index)->name() <<
"\n";
781 std::vector<const google::protobuf::FieldDescriptor*> fields;
782 reflection->ListFields(message, &fields);
784 for (
const google::protobuf::FieldDescriptor* field : fields)
786 if (field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE)
788 output_stream << std::string(depth * depth_spacing,
' ') << field->number() <<
": "
789 << field->name() <<
"\n";
791 if (field->is_repeated())
793 int32_t size = reflection->FieldSize(message, field);
795 for (int32_t index = 0; index < size; ++index)
798 reflection->GetRepeatedMessage(message, field, index);
800 output_stream << std::string((depth + 1) * depth_spacing,
' ') <<
"[" << index
804 output_stream << get_all_error_fields_in_message(sub_message, depth + 2);
810 reflection->GetMessage(message, field);
811 output_stream << get_all_error_fields_in_message(sub_message, depth + 1);
816 return output_stream.str();