DCCL v5
Loading...
Searching...
No Matches
test.cpp
1// Copyright 2026:
2// GobySoft, LLC (2013-)
3// Community contributors (see AUTHORS file)
4// File authors:
5// Toby Schneider <toby@gobysoft.org>
6//
7//
8// This file is part of the Dynamic Compact Control Language Library
9// ("DCCL").
10//
11// DCCL is free software: you can redistribute it and/or modify
12// it under the terms of the GNU Lesser General Public License as published by
13// the Free Software Foundation, either version 2.1 of the License, or
14// (at your option) any later version.
15//
16// DCCL is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19// GNU Lesser General Public License for more details.
20//
21// You should have received a copy of the GNU Lesser General Public License
22// along with DCCL. If not, see <http://www.gnu.org/licenses/>.
23// tests encryption/decryption via Codec::set_crypto_passphrase
24
25#include <cassert>
26#include <iostream>
27#include <string>
28
29#include "../../binary.h"
30#include "../../codec.h"
31#include "test.pb.h"
32
33using namespace dccl::test;
34
35namespace
36{
37CryptoMsg make_msg()
38{
39 CryptoMsg msg;
40 msg.set_source(5);
41 msg.set_x(1234);
42 msg.set_y(-5678);
43 msg.set_crc(0); // dummy value - overwritten by the dccl.crc32 codec
44 return msg;
45}
46
47bool same_contents(const CryptoMsg& a, const CryptoMsg& b)
48{
49 return a.source() == b.source() && a.x() == b.x() && a.y() == b.y();
50}
51
52std::string encode(dccl::Codec& codec, const google::protobuf::Message& msg)
53{
54 std::string bytes;
55 codec.encode(&bytes, msg);
56 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "encoded (hex): " << dccl::hex_encode(bytes)
57 << std::endl;
58 return bytes;
59}
60} // namespace
61
62int main(int argc, char* argv[])
63{
64 bool verbose = false;
65 for (int i = 1; i < argc; ++i)
66 {
67 if (argv[i] && argv[i][0] == '-' && argv[i][1] == 'v' && argv[i][2] == '\0')
68 verbose = true;
69 }
70
71 dccl::dlog.connect(verbose ? dccl::logger::ALL : dccl::logger::WARN_PLUS, &std::cerr);
72
73 const CryptoMsg msg_in = make_msg();
74
75 dccl::Codec plain_codec;
76 plain_codec.load<CryptoMsg>();
77 const std::string plain_bytes = encode(plain_codec, msg_in);
78
79 //
80 // round trip with the same passphrase
81 //
82 {
83 dccl::Codec codec;
84 codec.set_crypto_passphrase("my_passphrase");
85 codec.load<CryptoMsg>();
86
87 const std::string bytes = encode(codec, msg_in);
88 assert(bytes.size() == plain_bytes.size());
89 assert(bytes != plain_bytes);
90
91 // only the body is encrypted; the head is the nonce and stays in the clear
92 std::string head;
93 plain_codec.encode(&head, msg_in, /* header_only = */ true);
94 const std::size_t head_bytes = head.size();
95 assert(bytes.substr(0, head_bytes) == plain_bytes.substr(0, head_bytes));
96 assert(bytes.substr(head_bytes) != plain_bytes.substr(head_bytes));
97
98 CryptoMsg msg_out;
99 codec.decode(bytes, &msg_out);
100 assert(same_contents(msg_in, msg_out));
101
102 // the DCCL id remains readable without the passphrase
103 assert(plain_codec.id(bytes) == plain_codec.id<CryptoMsg>());
104 }
105
106 //
107 // a different passphrase must be rejected, not silently decoded as garbage
108 //
109 {
110 dccl::Codec enc_codec, dec_codec;
111 enc_codec.set_crypto_passphrase("my_passphrase");
112 dec_codec.set_crypto_passphrase("wrong_passphrase");
113 enc_codec.load<CryptoMsg>();
114 dec_codec.load<CryptoMsg>();
115
116 const std::string bytes = encode(enc_codec, msg_in);
117
118 bool rejected = false;
119 try
120 {
121 CryptoMsg msg_out;
122 dec_codec.decode(bytes, &msg_out);
123 }
124 catch (const std::exception& e)
125 {
126 rejected = true;
127 dccl::dlog.is(dccl::logger::INFO) &&
128 dccl::dlog << "expected decode failure: " << e.what() << std::endl;
129 }
130 assert(rejected);
131 }
132
133 //
134 // the dccl.crc32 field also catches corruption of an otherwise valid message
135 //
136 {
137 dccl::Codec codec;
138 codec.set_crypto_passphrase("my_passphrase");
139 codec.load<CryptoMsg>();
140
141 std::string bytes = encode(codec, msg_in);
142 bytes[bytes.size() - 1] ^= 0x01; // one bit of ciphertext, so one bit of body
143
144 bool rejected = false;
145 try
146 {
147 CryptoMsg msg_out;
148 codec.decode(bytes, &msg_out);
149 }
150 catch (const std::exception& e)
151 {
152 rejected = true;
153 dccl::dlog.is(dccl::logger::INFO) &&
154 dccl::dlog << "expected decode failure: " << e.what() << std::endl;
155 }
156 assert(rejected);
157 }
158
159 //
160 // ids listed in do_not_encrypt_ids are left in the clear
161 //
162 {
163 dccl::Codec codec;
164 codec.set_crypto_passphrase("my_passphrase", std::set<dccl::int32>{21});
165 codec.load<CryptoMsg>();
166 codec.load<PlaintextMsg>();
167
168 PlaintextMsg plain_in;
169 plain_in.set_source(5);
170 plain_in.set_x(1234);
171 plain_in.set_y(-5678);
172 plain_in.set_crc(0);
173
174 plain_codec.load<PlaintextMsg>();
175 assert(encode(codec, plain_in) == encode(plain_codec, plain_in));
176 assert(encode(codec, msg_in) != plain_bytes);
177 }
178
179 std::cout << "all tests passed" << std::endl;
180 return 0;
181}
The Dynamic CCL enCODer/DECoder. This is the main class you will use to load, encode and decode DCCL ...
Definition codec.h:61
int32 id() const
Gives the DCCL id (defined by the custom message option extension "(dccl.msg).id" in the ....
Definition codec.h:216
void encode(std::string *bytes, const google::protobuf::Message &msg, bool header_only=false, int user_id=-1)
Encodes a DCCL message.
Definition codec.cpp:306
void set_crypto_passphrase(const std::string &passphrase, const std::set< int32 > &do_not_encrypt_ids=std::set< int32 >())
Set a passphrase to be used when encoded messages to encrypt them and to decrypt messages after decod...
Definition codec.cpp:722
CharIterator decode(CharIterator begin, CharIterator end, ProtobufMessage *msg, bool header_only=false)
Decode a DCCL message when the type is known at compile time.
Definition codec.h:535
std::size_t load()
All messages must be explicited loaded and validated (size checks, option extensions checks,...
Definition codec.h:119
bool is(logger::Verbosity verbosity, logger::Group group=logger::GENERAL)
Indicates the verbosity of the Logger until the next std::flush or std::endl. The boolean return is u...
Definition logger.h:191
void connect(int verbosity_mask, Slot slot)
Connect the output of one or more given verbosities to a slot (function pointer or similar)
Definition logger.h:213
Unit test namespace.
Definition test.cpp:45
Dynamic Compact Control Language namespace.
Definition any.h:28
void hex_encode(CharIterator begin, CharIterator end, std::string *out, bool upper_case=false)
Encodes a (little-endian) hexadecimal string from a byte string. Index 0 of begin is written to index...
Definition binary.h:95