Line data Source code
1 : /*
2 : * Distributed under the OpenDDS License.
3 : * See: http://www.opendds.org/license.html
4 : */
5 :
6 : #include "CryptoBuiltInImpl.h"
7 :
8 : #include "CryptoBuiltInTypeSupportImpl.h"
9 : #include "CommonUtilities.h"
10 :
11 : #include "SSL/Utils.h"
12 :
13 : #include "dds/DdsDcpsInfrastructureC.h"
14 : #include "dds/DdsSecurityParamsC.h"
15 :
16 : #include "dds/DCPS/debug.h"
17 : #include "dds/DCPS/GuidUtils.h"
18 : #include "dds/DCPS/Message_Block_Ptr.h"
19 : #include "dds/DCPS/Serializer.h"
20 : #include "dds/DCPS/Util.h"
21 :
22 : #include "dds/DCPS/RTPS/MessageUtils.h"
23 : #include "dds/DCPS/RTPS/MessageTypes.h"
24 : #include "dds/DCPS/RTPS/MessageParser.h"
25 : #include "dds/DCPS/RTPS/RtpsCoreTypeSupportImpl.h"
26 :
27 : #include <openssl/err.h>
28 : #include <openssl/evp.h>
29 : #include <openssl/rand.h>
30 :
31 : #include "OpenSSL_init.h"
32 : #include "OpenSSL_legacy.h" // Must come after all other OpenSSL includes
33 :
34 : OPENDDS_BEGIN_VERSIONED_NAMESPACE_DECL
35 :
36 : using namespace DDS::Security;
37 : using namespace OpenDDS::Security::CommonUtilities;
38 : using OpenDDS::DCPS::Serializer;
39 : using OpenDDS::DCPS::Encoding;
40 : using OpenDDS::DCPS::ENDIAN_BIG;
41 : using OpenDDS::DCPS::align;
42 : using OpenDDS::DCPS::serialized_size;
43 : using OpenDDS::DCPS::Message_Block_Ptr;
44 : using OpenDDS::DCPS::security_debug;
45 :
46 : const Encoding common_encoding(Encoding::KIND_XCDR1, ENDIAN_BIG);
47 :
48 : namespace OpenDDS {
49 : namespace Security {
50 :
51 37 : CryptoBuiltInImpl::CryptoBuiltInImpl()
52 37 : : mutex_()
53 37 : , next_handle_(1)
54 : {
55 37 : openssl_init();
56 37 : }
57 :
58 40 : CryptoBuiltInImpl::~CryptoBuiltInImpl()
59 : {
60 37 : if (DCPS::security_debug.bookkeeping) {
61 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
62 : ACE_TEXT("CryptoBuiltInImpl::~CryptoBuiltInImpl keys_ %B encrypt_options_ %B participant_to_entity_ %B sessions_ %B derived_key_handles_ %B\n"),
63 : keys_.size(),
64 : encrypt_options_.size(),
65 : participant_to_entity_.size(),
66 : sessions_.size(),
67 : derived_key_handles_.size()));
68 : }
69 :
70 37 : openssl_cleanup();
71 40 : }
72 :
73 0 : bool CryptoBuiltInImpl::_is_a(const char* id)
74 : {
75 0 : return CryptoKeyFactory::_is_a(id)
76 0 : || CryptoKeyExchange::_is_a(id)
77 0 : || CryptoTransform::_is_a(id);
78 : }
79 :
80 0 : const char* CryptoBuiltInImpl::_interface_repository_id() const
81 : {
82 0 : return "";
83 : }
84 :
85 0 : bool CryptoBuiltInImpl::marshal(TAO_OutputCDR&)
86 : {
87 0 : return false;
88 : }
89 :
90 39 : NativeCryptoHandle CryptoBuiltInImpl::generate_handle()
91 : {
92 39 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
93 78 : return generate_handle_i();
94 39 : }
95 :
96 42 : NativeCryptoHandle CryptoBuiltInImpl::generate_handle_i()
97 : {
98 42 : return CommonUtilities::increment_handle(next_handle_);
99 : }
100 :
101 :
102 : // Key Factory
103 :
104 : namespace {
105 : const unsigned int KEY_LEN_BYTES = 32;
106 : const unsigned int BLOCK_LEN_BYTES = 16;
107 : const unsigned int MAX_BLOCKS_PER_SESSION = 1024;
108 :
109 19 : KeyMaterial_AES_GCM_GMAC make_key(unsigned int key_id, bool encrypt)
110 : {
111 19 : KeyMaterial_AES_GCM_GMAC k;
112 :
113 76 : for (unsigned int i = 0; i < TransformKindIndex; ++i) {
114 57 : k.transformation_kind[i] = 0;
115 : }
116 19 : k.transformation_kind[TransformKindIndex] =
117 19 : encrypt ? CRYPTO_TRANSFORMATION_KIND_AES256_GCM
118 : : CRYPTO_TRANSFORMATION_KIND_AES256_GMAC;
119 :
120 19 : k.master_salt.length(KEY_LEN_BYTES);
121 19 : RAND_bytes(k.master_salt.get_buffer(), KEY_LEN_BYTES);
122 :
123 95 : for (unsigned int i = 0; i < sizeof k.sender_key_id; ++i) {
124 76 : k.sender_key_id[i] = key_id >> (8 * i);
125 : }
126 :
127 19 : k.master_sender_key.length(KEY_LEN_BYTES);
128 19 : RAND_bytes(k.master_sender_key.get_buffer(), KEY_LEN_BYTES);
129 :
130 95 : for (unsigned int i = 0; i < sizeof k.receiver_specific_key_id; ++i) {
131 76 : k.receiver_specific_key_id[i] = 0;
132 : }
133 :
134 19 : k.master_receiver_specific_key.length(0);
135 19 : return k;
136 0 : }
137 :
138 : const unsigned submessage_key_index = 0;
139 : }
140 :
141 21 : ParticipantCryptoHandle CryptoBuiltInImpl::register_local_participant(
142 : IdentityHandle participant_identity,
143 : PermissionsHandle participant_permissions,
144 : const DDS::PropertySeq&,
145 : const ParticipantSecurityAttributes& participant_security_attributes,
146 : SecurityException& ex)
147 : {
148 21 : if (DDS::HANDLE_NIL == participant_identity) {
149 1 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid local participant ID");
150 1 : return DDS::HANDLE_NIL;
151 : }
152 20 : if (DDS::HANDLE_NIL == participant_permissions) {
153 1 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid local permissions");
154 1 : return DDS::HANDLE_NIL;
155 : }
156 :
157 19 : if (!participant_security_attributes.is_rtps_protected) {
158 2 : return DDS::HANDLE_NIL;
159 : }
160 :
161 17 : const NativeCryptoHandle h = generate_handle();
162 : const KeyMaterial key = make_key(h,
163 17 : participant_security_attributes.plugin_participant_attributes & PLUGIN_PARTICIPANT_SECURITY_ATTRIBUTES_FLAG_IS_RTPS_ENCRYPTED);
164 17 : KeySeq keys;
165 17 : DCPS::push_back(keys, key);
166 :
167 17 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
168 17 : keys_[h] = keys;
169 17 : if (DCPS::security_debug.bookkeeping) {
170 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
171 : ACE_TEXT("CryptoBuiltInImpl::register_local_participant keys_ (total %B)\n"),
172 : keys_.size()));
173 : }
174 17 : return h;
175 17 : }
176 :
177 13 : ParticipantCryptoHandle CryptoBuiltInImpl::register_matched_remote_participant(
178 : ParticipantCryptoHandle,
179 : IdentityHandle remote_participant_identity,
180 : PermissionsHandle remote_participant_permissions,
181 : SharedSecretHandle* shared_secret,
182 : SecurityException& ex)
183 : {
184 13 : if (DDS::HANDLE_NIL == remote_participant_identity) {
185 1 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid remote participant ID");
186 1 : return DDS::HANDLE_NIL;
187 : }
188 12 : if (DDS::HANDLE_NIL == remote_participant_permissions) {
189 0 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid remote participant permissions");
190 0 : return DDS::HANDLE_NIL;
191 : }
192 12 : if (!shared_secret) {
193 1 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid Shared Secret data");
194 1 : return DDS::HANDLE_NIL;
195 : }
196 :
197 11 : return generate_handle();
198 : }
199 :
200 : namespace {
201 :
202 0 : bool operator==(const TAO::String_Manager& lhs, const char* rhs)
203 : {
204 0 : return 0 == std::strcmp(lhs, rhs);
205 : }
206 :
207 2 : bool operator==(const char* lhs, const TAO::String_Manager& rhs)
208 : {
209 2 : return 0 == std::strcmp(lhs, rhs);
210 : }
211 :
212 11 : bool is_builtin_volatile(const DDS::PropertySeq& props)
213 : {
214 11 : for (unsigned int i = 0; i < props.length(); ++i) {
215 0 : if (props[i].name == "dds.sec.builtin_endpoint_name") {
216 0 : return props[i].value == "BuiltinParticipantVolatileMessageSecureWriter"
217 0 : || props[i].value == "BuiltinParticipantVolatileMessageSecureReader";
218 : }
219 : }
220 11 : return false;
221 : }
222 :
223 : const unsigned char VOLATILE_PLACEHOLDER_KIND[] = {DCPS::VENDORID_OCI[0], DCPS::VENDORID_OCI[1], 0, 1};
224 :
225 1 : bool is_volatile_placeholder(const KeyMaterial_AES_GCM_GMAC& keymat)
226 : {
227 1 : return 0 == std::memcmp(VOLATILE_PLACEHOLDER_KIND, keymat.transformation_kind, sizeof VOLATILE_PLACEHOLDER_KIND);
228 : }
229 :
230 0 : KeyMaterial_AES_GCM_GMAC make_volatile_placeholder()
231 : {
232 : // not an actual key, just used to identify the local datawriter/reader
233 : // crypto handle for a Built-In Participant Volatile Msg endpoint
234 0 : KeyMaterial_AES_GCM_GMAC k;
235 0 : std::memcpy(k.transformation_kind, VOLATILE_PLACEHOLDER_KIND, sizeof VOLATILE_PLACEHOLDER_KIND);
236 0 : std::memset(k.sender_key_id, 0, sizeof k.sender_key_id);
237 0 : std::memset(k.receiver_specific_key_id, 0, sizeof k.receiver_specific_key_id);
238 0 : return k;
239 : }
240 :
241 : struct PrivateKey {
242 : EVP_PKEY* pkey_;
243 0 : explicit PrivateKey(const KeyOctetSeq& key)
244 0 : : pkey_(EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, 0, key.get_buffer(), key.length()))
245 0 : {}
246 0 : explicit PrivateKey(const DDS::OctetSeq& key)
247 0 : : pkey_(EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, 0, key.get_buffer(), key.length()))
248 0 : {}
249 0 : operator EVP_PKEY*() { return pkey_; }
250 0 : ~PrivateKey() { EVP_PKEY_free(pkey_); }
251 : };
252 :
253 : struct DigestContext {
254 : EVP_MD_CTX* ctx_;
255 0 : DigestContext() : ctx_(EVP_MD_CTX_new()) {}
256 0 : operator EVP_MD_CTX*() { return ctx_; }
257 0 : ~DigestContext() { EVP_MD_CTX_free(ctx_); }
258 : };
259 :
260 0 : void hkdf(KeyOctetSeq& result, const DDS::OctetSeq_var& prefix,
261 : const char (&cookie)[17], const DDS::OctetSeq_var& suffix,
262 : const DDS::OctetSeq_var& data)
263 : {
264 0 : char* const cookie_buffer = const_cast<char*>(cookie); // OctetSeq has no const
265 0 : DDS::OctetSeq cookieSeq(16, 16, reinterpret_cast<CORBA::Octet*>(cookie_buffer));
266 0 : std::vector<const DDS::OctetSeq*> input(3);
267 0 : input[0] = prefix.ptr();
268 0 : input[1] = &cookieSeq;
269 0 : input[2] = suffix.ptr();
270 0 : DDS::OctetSeq key;
271 0 : if (SSL::hash(input, key) != 0) {
272 0 : return;
273 : }
274 :
275 0 : PrivateKey pkey(key);
276 0 : DigestContext ctx;
277 0 : const EVP_MD* const md = EVP_get_digestbyname("SHA256");
278 0 : if (EVP_DigestInit_ex(ctx, md, 0) != 1) {
279 0 : return;
280 : }
281 :
282 0 : if (EVP_DigestSignInit(ctx, 0, md, 0, pkey) != 1) {
283 0 : return;
284 : }
285 :
286 0 : if (EVP_DigestSignUpdate(ctx, data->get_buffer(), data->length()) != 1) {
287 0 : return;
288 : }
289 :
290 0 : size_t req = 0;
291 0 : if (EVP_DigestSignFinal(ctx, 0, &req) != 1) {
292 0 : return;
293 : }
294 :
295 0 : result.length(static_cast<unsigned int>(req));
296 0 : if (EVP_DigestSignFinal(ctx, result.get_buffer(), &req) != 1) {
297 0 : result.length(0);
298 : }
299 0 : }
300 :
301 : KeyMaterial_AES_GCM_GMAC
302 0 : make_volatile_key(const DDS::OctetSeq_var& challenge1,
303 : const DDS::OctetSeq_var& challenge2,
304 : const DDS::OctetSeq_var& sharedSec)
305 : {
306 : static const char KxSaltCookie[] = "keyexchange salt";
307 : static const char KxKeyCookie[] = "key exchange key";
308 0 : KeyMaterial_AES_GCM_GMAC k = {
309 : {0, 0, 0, CRYPTO_TRANSFORMATION_KIND_AES256_GCM},
310 : KeyOctetSeq(), {0, 0, 0, 0}, KeyOctetSeq(), {0, 0, 0, 0}, KeyOctetSeq()
311 0 : };
312 0 : hkdf(k.master_salt, challenge1, KxSaltCookie, challenge2, sharedSec);
313 0 : hkdf(k.master_sender_key, challenge2, KxKeyCookie, challenge1, sharedSec);
314 0 : return k;
315 0 : }
316 : }
317 :
318 7 : DatawriterCryptoHandle CryptoBuiltInImpl::register_local_datawriter(
319 : ParticipantCryptoHandle participant_crypto,
320 : const DDS::PropertySeq& properties,
321 : const EndpointSecurityAttributes& security_attributes,
322 : SecurityException&)
323 : {
324 7 : const NativeCryptoHandle h = generate_handle();
325 7 : const PluginEndpointSecurityAttributesMask plugin_attribs =
326 : security_attributes.plugin_endpoint_attributes;
327 7 : KeySeq keys;
328 :
329 7 : if (is_builtin_volatile(properties)) {
330 0 : DCPS::push_back(keys, make_volatile_placeholder());
331 :
332 : } else {
333 : // See Table 70 "register_local_datawriter" for the use of the key sequence
334 : // (requirements for which key appears first, etc.)
335 7 : bool used_h = false;
336 7 : if (security_attributes.is_submessage_protected) {
337 1 : const KeyMaterial key = make_key(h, plugin_attribs & FLAG_IS_SUBMESSAGE_ENCRYPTED);
338 1 : DCPS::push_back(keys, key);
339 1 : used_h = true;
340 1 : if (security_debug.bookkeeping && !security_debug.showkeys) {
341 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} CryptoBuiltInImpl::register_local_datawriter ")
342 : ACE_TEXT("created submessage key with id %C for LDWCH %d\n"),
343 : ctki_to_dds_string(key.sender_key_id).c_str(), h));
344 : }
345 1 : if (security_debug.showkeys) {
346 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {showkeys} CryptoBuiltInImpl::register_local_datawriter ")
347 : ACE_TEXT("created submessage key for LDWCH %d:\n%C"), h,
348 : to_dds_string(key).c_str()));
349 : }
350 1 : }
351 7 : if (security_attributes.is_payload_protected) {
352 0 : const unsigned int key_id = used_h ? generate_handle() : h;
353 0 : const KeyMaterial_AES_GCM_GMAC key = make_key(key_id, plugin_attribs & FLAG_IS_PAYLOAD_ENCRYPTED);
354 0 : DCPS::push_back(keys, key);
355 0 : if (security_debug.bookkeeping && !security_debug.showkeys) {
356 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} CryptoBuiltInImpl::register_local_datawriter ")
357 : ACE_TEXT("created payload key with id %C for LDWCH %d\n"),
358 : ctki_to_dds_string(key.sender_key_id).c_str(), h));
359 : }
360 0 : if (security_debug.showkeys) {
361 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {showkeys} CryptoBuiltInImpl::register_local_datawriter ")
362 : ACE_TEXT("created payload key for LDWCH %d:\n%C"), h,
363 : to_dds_string(key).c_str()));
364 : }
365 0 : }
366 : }
367 :
368 7 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
369 7 : keys_[h] = keys;
370 7 : if (DCPS::security_debug.bookkeeping) {
371 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
372 : ACE_TEXT("CryptoBuiltInImpl::register_local_datawriter keys_ (total %B)\n"),
373 : keys_.size()));
374 : }
375 7 : encrypt_options_[h] = security_attributes;
376 7 : if (DCPS::security_debug.bookkeeping) {
377 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
378 : ACE_TEXT("CryptoBuiltInImpl::register_local_datawriter encrypt_options_ (total %B)\n"),
379 : encrypt_options_.size()));
380 : }
381 :
382 7 : if (participant_crypto != DDS::HANDLE_NIL) {
383 2 : const EntityInfo e(DATAWRITER_SUBMESSAGE, h);
384 2 : participant_to_entity_.insert(std::make_pair(participant_crypto, e));
385 2 : if (DCPS::security_debug.bookkeeping) {
386 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
387 : ACE_TEXT("CryptoBuiltInImpl::register_local_datawriter participant_to_entity_ (total %B)\n"),
388 : participant_to_entity_.size()));
389 : }
390 : }
391 :
392 7 : return h;
393 7 : }
394 :
395 4 : DatareaderCryptoHandle CryptoBuiltInImpl::register_matched_remote_datareader(
396 : DatawriterCryptoHandle local_datawriter_crypto_handle,
397 : ParticipantCryptoHandle remote_participant_crypto,
398 : SharedSecretHandle* shared_secret,
399 : bool /*relay_only*/,
400 : SecurityException& ex)
401 : {
402 4 : if (DDS::HANDLE_NIL == local_datawriter_crypto_handle) {
403 1 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid Local DataWriter Crypto Handle");
404 1 : return DDS::HANDLE_NIL;
405 : }
406 3 : if (DDS::HANDLE_NIL == remote_participant_crypto) {
407 1 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid Remote Participant Crypto Handle");
408 1 : return DDS::HANDLE_NIL;
409 : }
410 2 : if (!shared_secret) {
411 1 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid Shared Secret Handle");
412 1 : return DDS::HANDLE_NIL;
413 : }
414 :
415 1 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
416 1 : const KeyTable_t::const_iterator iter = keys_.find(local_datawriter_crypto_handle);
417 1 : if (iter == keys_.end()) {
418 0 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid Local DataWriter Crypto Handle");
419 0 : return DDS::HANDLE_NIL;
420 : }
421 :
422 1 : const KeySeq& dw_keys = iter->second;
423 1 : const bool use_derived_key = dw_keys.length() == 1 && is_volatile_placeholder(dw_keys[0]);
424 :
425 1 : const HandlePair_t input_handles = std::make_pair(remote_participant_crypto, local_datawriter_crypto_handle);
426 : const DerivedKeyIndex_t::iterator existing_handle_iter =
427 1 : use_derived_key ? derived_key_handles_.find(input_handles) : derived_key_handles_.end();
428 : const DatareaderCryptoHandle h =
429 1 : (existing_handle_iter == derived_key_handles_.end())
430 1 : ? generate_handle_i() : existing_handle_iter->second;
431 :
432 1 : if (use_derived_key) {
433 : // Create a key from SharedSecret and track it as if Key Exchange happened
434 0 : KeySeq dr_keys(1);
435 0 : dr_keys.length(1);
436 0 : dr_keys[0] = make_volatile_key(shared_secret->challenge1(),
437 0 : shared_secret->challenge2(),
438 0 : shared_secret->sharedSecret());
439 0 : if (!dr_keys[0].master_salt.length()
440 0 : || !dr_keys[0].master_sender_key.length()) {
441 0 : CommonUtilities::set_security_error(ex, -1, 0, "Couldn't create key for "
442 : "volatile remote reader");
443 0 : return DDS::HANDLE_NIL;
444 : }
445 0 : if (security_debug.bookkeeping && !security_debug.showkeys) {
446 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
447 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datareader ")
448 : ACE_TEXT("created volatile key for RDRCH %d\n"), h));
449 : }
450 0 : if (security_debug.showkeys) {
451 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {showkeys} ")
452 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datareader ")
453 : ACE_TEXT("created volatile key for RDRCH %d:\n%C"), h,
454 : to_dds_string(dr_keys[0]).c_str()));
455 : }
456 0 : keys_[h] = dr_keys;
457 0 : if (DCPS::security_debug.bookkeeping) {
458 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
459 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datareader keys_ (total %B)\n"),
460 : keys_.size()));
461 : }
462 0 : if (existing_handle_iter != derived_key_handles_.end()) {
463 0 : sessions_.erase(std::make_pair(h, submessage_key_index));
464 0 : if (DCPS::security_debug.bookkeeping) {
465 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
466 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datareader sessions_ (total %B)\n"),
467 : sessions_.size()));
468 : }
469 : } else {
470 0 : derived_key_handles_[input_handles] = h;
471 0 : if (DCPS::security_debug.bookkeeping) {
472 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
473 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datareader derived_key_handles_ (total %B)\n"),
474 : derived_key_handles_.size()));
475 : }
476 : }
477 0 : }
478 :
479 1 : const EntityInfo e(DATAREADER_SUBMESSAGE, h);
480 1 : participant_to_entity_.insert(std::make_pair(remote_participant_crypto, e));
481 1 : if (DCPS::security_debug.bookkeeping) {
482 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
483 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datareader participant_to_entity_ (total %B)\n"),
484 : participant_to_entity_.size()));
485 : }
486 1 : encrypt_options_[h] = encrypt_options_[local_datawriter_crypto_handle];
487 1 : if (DCPS::security_debug.bookkeeping) {
488 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
489 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datareader encrypt_options_ (total %B)\n"),
490 : encrypt_options_.size()));
491 : }
492 1 : return h;
493 1 : }
494 :
495 4 : DatareaderCryptoHandle CryptoBuiltInImpl::register_local_datareader(
496 : ParticipantCryptoHandle participant_crypto,
497 : const DDS::PropertySeq& properties,
498 : const EndpointSecurityAttributes& security_attributes,
499 : SecurityException&)
500 : {
501 4 : const NativeCryptoHandle h = generate_handle();
502 4 : const PluginEndpointSecurityAttributesMask plugin_attribs = security_attributes.plugin_endpoint_attributes;
503 4 : KeySeq keys;
504 :
505 4 : if (is_builtin_volatile(properties)) {
506 0 : DCPS::push_back(keys, make_volatile_placeholder());
507 :
508 4 : } else if (security_attributes.is_submessage_protected) {
509 1 : const KeyMaterial key = make_key(h, plugin_attribs & FLAG_IS_SUBMESSAGE_ENCRYPTED);
510 1 : if (security_debug.bookkeeping && !security_debug.showkeys) {
511 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} CryptoBuiltInImpl::register_local_datareader ")
512 : ACE_TEXT("created submessage key with id %C for LDRCH %d\n"),
513 : ctki_to_dds_string(key.sender_key_id).c_str(), h));
514 : }
515 1 : if (security_debug.showkeys) {
516 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {showkeys} CryptoBuiltInImpl::register_local_datareader ")
517 : ACE_TEXT("created submessage key for LDRCH %d:\n%C"), h,
518 : to_dds_string(key).c_str()));
519 : }
520 1 : DCPS::push_back(keys, key);
521 1 : }
522 :
523 4 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
524 4 : keys_[h] = keys;
525 4 : if (DCPS::security_debug.bookkeeping) {
526 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
527 : ACE_TEXT("CryptoBuiltInImpl::register_local_datareader keys_ (total %B)\n"),
528 : keys_.size()));
529 : }
530 4 : encrypt_options_[h] = security_attributes;
531 4 : if (DCPS::security_debug.bookkeeping) {
532 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
533 : ACE_TEXT("CryptoBuiltInImpl::register_local_datareader encrypt_options_ (total %B)\n"),
534 : encrypt_options_.size()));
535 : }
536 :
537 4 : if (participant_crypto != DDS::HANDLE_NIL) {
538 2 : const EntityInfo e(DATAREADER_SUBMESSAGE, h);
539 2 : participant_to_entity_.insert(std::make_pair(participant_crypto, e));
540 2 : if (DCPS::security_debug.bookkeeping) {
541 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
542 : ACE_TEXT("CryptoBuiltInImpl::register_local_datareader participant_to_entity_ (total %B)\n"),
543 : participant_to_entity_.size()));
544 : }
545 : }
546 :
547 4 : return h;
548 4 : }
549 :
550 5 : DatawriterCryptoHandle CryptoBuiltInImpl::register_matched_remote_datawriter(
551 : DatareaderCryptoHandle local_datareader_crypto_handle,
552 : ParticipantCryptoHandle remote_participant_crypto,
553 : SharedSecretHandle* shared_secret,
554 : SecurityException& ex)
555 : {
556 5 : if (DDS::HANDLE_NIL == local_datareader_crypto_handle) {
557 1 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid Local DataReader Crypto Handle");
558 1 : return DDS::HANDLE_NIL;
559 : }
560 4 : if (DDS::HANDLE_NIL == remote_participant_crypto) {
561 1 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid Remote Participant Crypto Handle");
562 1 : return DDS::HANDLE_NIL;
563 : }
564 3 : if (!shared_secret) {
565 1 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid Shared Secret Handle");
566 1 : return DDS::HANDLE_NIL;
567 : }
568 :
569 2 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
570 2 : const KeyTable_t::const_iterator iter = keys_.find(local_datareader_crypto_handle);
571 2 : if (iter == keys_.end()) {
572 0 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid Local DataReader Crypto Handle");
573 0 : return DDS::HANDLE_NIL;
574 : }
575 :
576 2 : const KeySeq& dr_keys = iter->second;
577 2 : const bool use_derived_key = dr_keys.length() == 1 && is_volatile_placeholder(dr_keys[0]);
578 :
579 2 : const HandlePair_t input_handles = std::make_pair(remote_participant_crypto, local_datareader_crypto_handle);
580 : const DerivedKeyIndex_t::iterator existing_handle_iter =
581 2 : use_derived_key ? derived_key_handles_.find(input_handles) : derived_key_handles_.end();
582 : const DatareaderCryptoHandle h =
583 2 : (existing_handle_iter == derived_key_handles_.end())
584 2 : ? generate_handle_i() : existing_handle_iter->second;
585 :
586 2 : if (use_derived_key) {
587 : // Create a key from SharedSecret and track it as if Key Exchange happened
588 0 : KeySeq dw_keys(1);
589 0 : dw_keys.length(1);
590 0 : dw_keys[0] = make_volatile_key(shared_secret->challenge1(),
591 0 : shared_secret->challenge2(),
592 0 : shared_secret->sharedSecret());
593 0 : if (!dw_keys[0].master_salt.length()
594 0 : || !dw_keys[0].master_sender_key.length()) {
595 0 : CommonUtilities::set_security_error(ex, -1, 0, "Couldn't create key for "
596 : "volatile remote writer");
597 0 : return DDS::HANDLE_NIL;
598 : }
599 0 : if (security_debug.bookkeeping && !security_debug.showkeys) {
600 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
601 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datawriter ")
602 : ACE_TEXT("created volatile key for RDWCH %d\n"), h));
603 : }
604 0 : if (security_debug.showkeys) {
605 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {showkeys} ")
606 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datawriter ")
607 : ACE_TEXT("created volatile key for RDWCH %d:\n%C"), h,
608 : to_dds_string(dw_keys[0]).c_str()));
609 : }
610 0 : keys_[h] = dw_keys;
611 0 : if (DCPS::security_debug.bookkeeping) {
612 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
613 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datawriter keys_ (total %B)\n"),
614 : keys_.size()));
615 : }
616 0 : if (existing_handle_iter != derived_key_handles_.end()) {
617 0 : sessions_.erase(std::make_pair(h, submessage_key_index));
618 0 : if (DCPS::security_debug.bookkeeping) {
619 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
620 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datawriter sessions_ (total %B)\n"),
621 : sessions_.size()));
622 : }
623 : } else {
624 0 : derived_key_handles_[input_handles] = h;
625 0 : if (DCPS::security_debug.bookkeeping) {
626 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
627 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datawriter derived_key_handles_ (total %B)\n"),
628 : derived_key_handles_.size()));
629 : }
630 : }
631 0 : }
632 :
633 2 : const EntityInfo e(DATAWRITER_SUBMESSAGE, h);
634 2 : participant_to_entity_.insert(std::make_pair(remote_participant_crypto, e));
635 2 : if (DCPS::security_debug.bookkeeping) {
636 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
637 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datawriter participant_to_entity_ (total %B)\n"),
638 : participant_to_entity_.size()));
639 : }
640 2 : encrypt_options_[h] = encrypt_options_[local_datareader_crypto_handle];
641 2 : if (DCPS::security_debug.bookkeeping) {
642 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
643 : ACE_TEXT("CryptoBuiltInImpl::register_matched_remote_datawriter encrypt_options_ (total %B)\n"),
644 : encrypt_options_.size()));
645 : }
646 2 : return h;
647 2 : }
648 :
649 1 : bool CryptoBuiltInImpl::unregister_participant(ParticipantCryptoHandle handle, SecurityException& ex)
650 : {
651 1 : if (DDS::HANDLE_NIL == handle) {
652 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid Crypto Handle");
653 : }
654 :
655 1 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
656 1 : clear_common_data(handle);
657 1 : for (DerivedKeyIndex_t::iterator it = derived_key_handles_.lower_bound(std::make_pair(handle, 0));
658 1 : it != derived_key_handles_.end() && it->first.first == handle; derived_key_handles_.erase(it++)) {
659 0 : if (DCPS::security_debug.bookkeeping) {
660 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
661 : ACE_TEXT("CryptoBuiltInImpl::unregister_participant derived_key_handles_ (total %B)\n"),
662 : derived_key_handles_.size()));
663 : }
664 : }
665 1 : return true;
666 1 : }
667 :
668 3 : void CryptoBuiltInImpl::clear_common_data(NativeCryptoHandle handle)
669 : {
670 3 : keys_.erase(handle);
671 3 : if (DCPS::security_debug.bookkeeping) {
672 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
673 : ACE_TEXT("CryptoBuiltInImpl::clear_common_data keys_ (total %B)\n"),
674 : keys_.size()));
675 : }
676 3 : for (SessionTable_t::iterator st_iter = sessions_.lower_bound(std::make_pair(handle, 0));
677 3 : st_iter != sessions_.end() && st_iter->first.first == handle;
678 0 : sessions_.erase(st_iter++)) {
679 0 : if (DCPS::security_debug.bookkeeping) {
680 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
681 : ACE_TEXT("CryptoBuiltInImpl::clear_common_data sessions_ (total %B)\n"),
682 : sessions_.size()));
683 : }
684 : }
685 3 : }
686 :
687 2 : void CryptoBuiltInImpl::clear_endpoint_data(NativeCryptoHandle handle)
688 : {
689 2 : clear_common_data(handle);
690 2 : encrypt_options_.erase(handle);
691 2 : if (DCPS::security_debug.bookkeeping) {
692 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
693 : ACE_TEXT("CryptoBuiltInImpl::clear_endpoint_data encrypt_options_ (total %B)\n"),
694 : encrypt_options_.size()));
695 : }
696 :
697 : typedef std::multimap<ParticipantCryptoHandle, EntityInfo>::iterator iter_t;
698 4 : for (iter_t it = participant_to_entity_.begin(); it != participant_to_entity_.end();) {
699 2 : if (it->second.handle_ == handle) {
700 2 : participant_to_entity_.erase(it++);
701 2 : if (DCPS::security_debug.bookkeeping) {
702 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
703 : ACE_TEXT("CryptoBuiltInImpl::clear_endpoint_data participant_to_entity_ (total %B)\n"),
704 : participant_to_entity_.size()));
705 : }
706 : } else {
707 0 : ++it;
708 : }
709 : }
710 2 : }
711 :
712 1 : bool CryptoBuiltInImpl::unregister_datawriter(DatawriterCryptoHandle handle, SecurityException& ex)
713 : {
714 1 : if (DDS::HANDLE_NIL == handle) {
715 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid Crypto Handle");
716 : }
717 :
718 1 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
719 1 : clear_endpoint_data(handle);
720 1 : return true;
721 1 : }
722 :
723 1 : bool CryptoBuiltInImpl::unregister_datareader(DatareaderCryptoHandle handle, SecurityException& ex)
724 : {
725 1 : if (DDS::HANDLE_NIL == handle) {
726 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid Crypto Handle");
727 : }
728 :
729 1 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
730 1 : clear_endpoint_data(handle);
731 1 : return true;
732 1 : }
733 :
734 :
735 : // Key Exchange
736 :
737 : namespace {
738 : const char Crypto_Token_Class_Id[] = "DDS:Crypto:AES_GCM_GMAC";
739 : const char Token_KeyMat_Name[] = "dds.cryp.keymat";
740 :
741 3 : const char* to_mb(const unsigned char* buffer)
742 : {
743 3 : return reinterpret_cast<const char*>(buffer);
744 : }
745 :
746 1 : ParticipantCryptoTokenSeq keys_to_tokens(const KeyMaterial_AES_GCM_GMAC_Seq& keys)
747 : {
748 1 : ParticipantCryptoTokenSeq tokens;
749 2 : for (unsigned int i = 0; i < keys.length(); ++i) {
750 1 : CryptoToken t;
751 1 : t.class_id = Crypto_Token_Class_Id;
752 1 : t.binary_properties.length(1);
753 1 : DDS::BinaryProperty_t& p = t.binary_properties[0];
754 1 : p.name = Token_KeyMat_Name;
755 1 : p.propagate = true;
756 1 : const size_t size = serialized_size(common_encoding, keys[i]);
757 1 : p.value.length(static_cast<unsigned int>(size));
758 1 : ACE_Message_Block mb(to_mb(p.value.get_buffer()), size);
759 1 : Serializer ser(&mb, common_encoding);
760 1 : if (ser << keys[i]) {
761 1 : DCPS::push_back(tokens, t);
762 : } else {
763 0 : ACE_ERROR((LM_ERROR,
764 : "(%P|%t) ERROR: keys_to_tokens: Failed to serialize\n"));
765 : }
766 1 : }
767 1 : return tokens;
768 0 : }
769 :
770 4 : KeyMaterial_AES_GCM_GMAC_Seq tokens_to_keys(const ParticipantCryptoTokenSeq& tokens)
771 : {
772 4 : KeyMaterial_AES_GCM_GMAC_Seq keys;
773 5 : for (unsigned int i = 0; i < tokens.length(); ++i) {
774 1 : const CryptoToken& t = tokens[i];
775 1 : if (Crypto_Token_Class_Id == t.class_id) {
776 1 : for (unsigned int j = 0; j < t.binary_properties.length(); ++j) {
777 1 : const DDS::BinaryProperty_t& p = t.binary_properties[j];
778 1 : if (Token_KeyMat_Name == p.name) {
779 1 : ACE_Message_Block mb(to_mb(p.value.get_buffer()), p.value.length());
780 1 : mb.wr_ptr(p.value.length());
781 1 : Serializer ser(&mb, common_encoding);
782 1 : KeyMaterial_AES_GCM_GMAC key;
783 1 : if (ser >> key) {
784 1 : DCPS::push_back(keys, key);
785 : } else {
786 0 : ACE_ERROR((LM_ERROR,
787 : "(%P|%t) ERROR: tokens_to_keys: Failed to deserialize\n"));
788 : }
789 1 : break;
790 1 : }
791 : }
792 : }
793 : }
794 4 : return keys;
795 0 : }
796 : }
797 :
798 2 : bool CryptoBuiltInImpl::create_local_participant_crypto_tokens(
799 : ParticipantCryptoTokenSeq& local_participant_crypto_tokens,
800 : ParticipantCryptoHandle local_participant_crypto,
801 : ParticipantCryptoHandle remote_participant_crypto,
802 : SecurityException& ex)
803 : {
804 2 : if (DDS::HANDLE_NIL == local_participant_crypto) {
805 1 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid local participant handle");
806 : }
807 1 : if (DDS::HANDLE_NIL == remote_participant_crypto) {
808 1 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid remote participant handle");
809 : }
810 :
811 0 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
812 0 : const KeyTable_t::const_iterator iter = keys_.find(local_participant_crypto);
813 0 : if (iter != keys_.end()) {
814 0 : local_participant_crypto_tokens = keys_to_tokens(iter->second);
815 : } else {
816 : // There may not be any keys_ for this participant (depends on config)
817 0 : local_participant_crypto_tokens.length(0);
818 : }
819 :
820 0 : return true;
821 0 : }
822 :
823 0 : bool CryptoBuiltInImpl::have_local_participant_crypto_tokens(
824 : DDS::Security::ParticipantCryptoHandle local_participant_crypto,
825 : DDS::Security::ParticipantCryptoHandle remote_participant_crypto)
826 : {
827 0 : if (DDS::HANDLE_NIL == local_participant_crypto) {
828 0 : return false;
829 : }
830 0 : if (DDS::HANDLE_NIL == remote_participant_crypto) {
831 0 : return false;
832 : }
833 :
834 0 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
835 0 : const KeyTable_t::const_iterator iter = keys_.find(local_participant_crypto);
836 0 : if (iter == keys_.end()) {
837 0 : return false;
838 : }
839 0 : return iter->second.length();
840 0 : }
841 :
842 1 : bool CryptoBuiltInImpl::set_remote_participant_crypto_tokens(
843 : ParticipantCryptoHandle local_participant_crypto,
844 : ParticipantCryptoHandle remote_participant_crypto,
845 : const ParticipantCryptoTokenSeq& remote_participant_tokens,
846 : SecurityException& ex)
847 : {
848 1 : if (DDS::HANDLE_NIL == local_participant_crypto) {
849 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid local participant handle");
850 : }
851 1 : if (DDS::HANDLE_NIL == remote_participant_crypto) {
852 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid remote participant handle");
853 : }
854 :
855 1 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
856 1 : keys_[remote_participant_crypto] = tokens_to_keys(remote_participant_tokens);
857 1 : if (DCPS::security_debug.bookkeeping) {
858 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
859 : ACE_TEXT("CryptoBuiltInImpl::set_remote_participant_crypto_tokens keys_ (total %B)\n"),
860 : keys_.size()));
861 : }
862 1 : return true;
863 1 : }
864 :
865 0 : bool CryptoBuiltInImpl::have_remote_participant_crypto_tokens(
866 : DDS::Security::ParticipantCryptoHandle local_participant_crypto,
867 : DDS::Security::ParticipantCryptoHandle remote_participant_crypto)
868 : {
869 0 : if (DDS::HANDLE_NIL == local_participant_crypto) {
870 0 : return false;
871 : }
872 0 : if (DDS::HANDLE_NIL == remote_participant_crypto) {
873 0 : return false;
874 : }
875 :
876 0 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
877 0 : const KeyTable_t::const_iterator iter = keys_.find(remote_participant_crypto);
878 0 : if (iter == keys_.end()) {
879 0 : return false;
880 : }
881 0 : return iter->second.length();
882 0 : }
883 :
884 4 : bool CryptoBuiltInImpl::create_local_datawriter_crypto_tokens(
885 : DatawriterCryptoTokenSeq& local_datawriter_crypto_tokens,
886 : DatawriterCryptoHandle local_datawriter_crypto,
887 : DatareaderCryptoHandle remote_datareader_crypto,
888 : SecurityException& ex)
889 : {
890 4 : if (DDS::HANDLE_NIL == local_datawriter_crypto) {
891 1 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid local writer handle");
892 : }
893 3 : if (DDS::HANDLE_NIL == remote_datareader_crypto) {
894 1 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid remote reader handle");
895 : }
896 :
897 2 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
898 2 : const KeyTable_t::const_iterator iter = keys_.find(local_datawriter_crypto);
899 2 : if (iter != keys_.end()) {
900 1 : local_datawriter_crypto_tokens = keys_to_tokens(iter->second);
901 : } else {
902 1 : local_datawriter_crypto_tokens.length(0);
903 : }
904 :
905 2 : return true;
906 2 : }
907 :
908 0 : bool CryptoBuiltInImpl::have_local_datawriter_crypto_tokens(
909 : DatawriterCryptoHandle local_datawriter_crypto,
910 : DatareaderCryptoHandle remote_datareader_crypto)
911 : {
912 0 : if (DDS::HANDLE_NIL == local_datawriter_crypto) {
913 0 : return false;
914 : }
915 0 : if (DDS::HANDLE_NIL == remote_datareader_crypto) {
916 0 : return false;
917 : }
918 :
919 0 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
920 0 : const KeyTable_t::const_iterator iter = keys_.find(local_datawriter_crypto);
921 0 : if (iter == keys_.end()) {
922 0 : return false;
923 : }
924 0 : return iter->second.length();
925 0 : }
926 :
927 2 : bool CryptoBuiltInImpl::set_remote_datawriter_crypto_tokens(
928 : DatareaderCryptoHandle local_datareader_crypto,
929 : DatawriterCryptoHandle remote_datawriter_crypto,
930 : const DatawriterCryptoTokenSeq& remote_datawriter_tokens,
931 : SecurityException& ex)
932 : {
933 2 : if (DDS::HANDLE_NIL == local_datareader_crypto) {
934 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid local datareader handle");
935 : }
936 2 : if (DDS::HANDLE_NIL == remote_datawriter_crypto) {
937 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid remote datawriter handle");
938 : }
939 :
940 2 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
941 2 : keys_[remote_datawriter_crypto] = tokens_to_keys(remote_datawriter_tokens);
942 2 : if (DCPS::security_debug.bookkeeping) {
943 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
944 : ACE_TEXT("CryptoBuiltInImpl::set_remote_datawriter_crypto_tokens keys_ (total %B)\n"),
945 : keys_.size()));
946 : }
947 2 : return true;
948 2 : }
949 :
950 0 : bool CryptoBuiltInImpl::have_remote_datawriter_crypto_tokens(
951 : DatareaderCryptoHandle local_datareader_crypto,
952 : DatawriterCryptoHandle remote_datawriter_crypto)
953 : {
954 0 : if (DDS::HANDLE_NIL == local_datareader_crypto) {
955 0 : return false;
956 : }
957 0 : if (DDS::HANDLE_NIL == remote_datawriter_crypto) {
958 0 : return false;
959 : }
960 :
961 0 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
962 0 : const KeyTable_t::const_iterator iter = keys_.find(remote_datawriter_crypto);
963 0 : if (iter == keys_.end()) {
964 0 : return false;
965 : }
966 0 : return iter->second.length();
967 0 : }
968 :
969 3 : bool CryptoBuiltInImpl::create_local_datareader_crypto_tokens(
970 : DatareaderCryptoTokenSeq& local_datareader_crypto_tokens,
971 : DatareaderCryptoHandle local_datareader_crypto,
972 : DatawriterCryptoHandle remote_datawriter_crypto,
973 : SecurityException& ex)
974 : {
975 3 : if (DDS::HANDLE_NIL == local_datareader_crypto) {
976 1 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid local reader handle");
977 : }
978 2 : if (DDS::HANDLE_NIL == remote_datawriter_crypto) {
979 1 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid remote writer handle");
980 : }
981 :
982 1 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
983 1 : const KeyTable_t::const_iterator iter = keys_.find(local_datareader_crypto);
984 1 : if (iter != keys_.end()) {
985 0 : local_datareader_crypto_tokens = keys_to_tokens(iter->second);
986 : } else {
987 1 : local_datareader_crypto_tokens.length(0);
988 : }
989 :
990 1 : return true;
991 1 : }
992 :
993 0 : bool CryptoBuiltInImpl::have_local_datareader_crypto_tokens(
994 : DatareaderCryptoHandle local_datareader_crypto,
995 : DatawriterCryptoHandle remote_datawriter_crypto)
996 : {
997 0 : if (DDS::HANDLE_NIL == local_datareader_crypto) {
998 0 : return false;
999 : }
1000 0 : if (DDS::HANDLE_NIL == remote_datawriter_crypto) {
1001 0 : return false;
1002 : }
1003 :
1004 0 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
1005 0 : const KeyTable_t::const_iterator iter = keys_.find(local_datareader_crypto);
1006 0 : if (iter == keys_.end()) {
1007 0 : return false;
1008 : }
1009 0 : return iter->second.length();
1010 0 : }
1011 :
1012 1 : bool CryptoBuiltInImpl::set_remote_datareader_crypto_tokens(
1013 : DatawriterCryptoHandle local_datawriter_crypto,
1014 : DatareaderCryptoHandle remote_datareader_crypto,
1015 : const DatareaderCryptoTokenSeq& remote_datareader_tokens,
1016 : SecurityException& ex)
1017 : {
1018 1 : if (DDS::HANDLE_NIL == local_datawriter_crypto) {
1019 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid local datawriter handle");
1020 : }
1021 1 : if (DDS::HANDLE_NIL == remote_datareader_crypto) {
1022 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid remote datareader handle");
1023 : }
1024 :
1025 1 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
1026 1 : keys_[remote_datareader_crypto] = tokens_to_keys(remote_datareader_tokens);
1027 1 : if (DCPS::security_debug.bookkeeping) {
1028 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {bookkeeping} ")
1029 : ACE_TEXT("CryptoBuiltInImpl::set_remote_datareader_crypto_tokens keys_ (total %B)\n"),
1030 : keys_.size()));
1031 : }
1032 1 : return true;
1033 1 : }
1034 :
1035 0 : bool CryptoBuiltInImpl::have_remote_datareader_crypto_tokens(
1036 : DatawriterCryptoHandle local_datawriter_crypto,
1037 : DatareaderCryptoHandle remote_datareader_crypto)
1038 : {
1039 0 : if (DDS::HANDLE_NIL == local_datawriter_crypto) {
1040 0 : return false;
1041 : }
1042 0 : if (DDS::HANDLE_NIL == remote_datareader_crypto) {
1043 0 : return false;
1044 : }
1045 :
1046 0 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
1047 0 : const KeyTable_t::const_iterator iter = keys_.find(remote_datareader_crypto);
1048 0 : if (iter == keys_.end()) {
1049 0 : return false;
1050 : }
1051 0 : return iter->second.length();
1052 0 : }
1053 :
1054 0 : bool CryptoBuiltInImpl::return_crypto_tokens(const CryptoTokenSeq&, SecurityException&)
1055 : {
1056 0 : return true;
1057 : }
1058 :
1059 :
1060 : // Transform
1061 :
1062 : namespace {
1063 0 : bool encrypts(const KeyMaterial_AES_GCM_GMAC& k)
1064 : {
1065 0 : const CryptoTransformKind& kind = k.transformation_kind;
1066 0 : return kind[0] == 0 && kind[1] == 0 && kind[2] == 0
1067 0 : && (kind[TransformKindIndex] == CRYPTO_TRANSFORMATION_KIND_AES128_GCM ||
1068 0 : kind[TransformKindIndex] == CRYPTO_TRANSFORMATION_KIND_AES256_GCM);
1069 : }
1070 :
1071 0 : bool authenticates(const KeyMaterial_AES_GCM_GMAC& k)
1072 : {
1073 0 : const CryptoTransformKind& kind = k.transformation_kind;
1074 0 : return kind[0] == 0 && kind[1] == 0 && kind[2] == 0
1075 0 : && (kind[TransformKindIndex] == CRYPTO_TRANSFORMATION_KIND_AES128_GMAC ||
1076 0 : kind[TransformKindIndex] == CRYPTO_TRANSFORMATION_KIND_AES256_GMAC);
1077 : }
1078 :
1079 : struct CipherContext {
1080 : EVP_CIPHER_CTX* ctx_;
1081 0 : CipherContext() : ctx_(EVP_CIPHER_CTX_new()) {}
1082 0 : operator EVP_CIPHER_CTX*() { return ctx_; }
1083 0 : ~CipherContext() { EVP_CIPHER_CTX_free(ctx_); }
1084 : };
1085 :
1086 0 : bool inc32(unsigned char* a)
1087 : {
1088 0 : for (int i = 0; i < 4; ++i) {
1089 0 : if (a[i] != 0xff) {
1090 0 : ++a[i];
1091 0 : return false;
1092 : }
1093 : }
1094 0 : std::fill(a, a + 4, 0);
1095 0 : return true;
1096 : }
1097 :
1098 : const size_t CRYPTO_CONTENT_ADDED_LENGTH = 4;
1099 : const size_t CRYPTO_HEADER_LENGTH = 20;
1100 : }
1101 :
1102 3 : bool CryptoBuiltInImpl::encode_serialized_payload(
1103 : DDS::OctetSeq& encoded_buffer,
1104 : DDS::OctetSeq& /*extra_inline_qos*/,
1105 : const DDS::OctetSeq& plain_buffer,
1106 : DatawriterCryptoHandle sending_datawriter_crypto,
1107 : SecurityException& ex)
1108 : {
1109 3 : if (DDS::HANDLE_NIL == sending_datawriter_crypto) {
1110 1 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid datawriter handle");
1111 : }
1112 :
1113 2 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
1114 2 : const KeyTable_t::const_iterator keys_iter = keys_.find(sending_datawriter_crypto);
1115 2 : const EncryptOptions_t::const_iterator eo_iter = encrypt_options_.find(sending_datawriter_crypto);
1116 2 : if (eo_iter == encrypt_options_.end()) {
1117 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Datawriter handle lacks encrypt options");
1118 : }
1119 2 : if (keys_iter == keys_.end() || !eo_iter->second.payload_) {
1120 2 : encoded_buffer = plain_buffer;
1121 2 : return true;
1122 : }
1123 :
1124 0 : const KeySeq& keyseq = keys_iter->second;
1125 0 : if (!keyseq.length()) {
1126 0 : encoded_buffer = plain_buffer;
1127 0 : return true;
1128 : }
1129 :
1130 : bool ok;
1131 : CryptoHeader header;
1132 0 : CryptoFooter footer;
1133 0 : DDS::OctetSeq out;
1134 0 : const DDS::OctetSeq* pOut = &plain_buffer;
1135 : // see register_local_datawriter for the assignment of key indexes in the seq
1136 0 : const unsigned int key_idx = keyseq.length() >= 2 ? 1 : 0;
1137 0 : const KeyId_t sKey = std::make_pair(sending_datawriter_crypto, key_idx);
1138 :
1139 0 : if (encrypts(keyseq[key_idx])) {
1140 0 : ok = encrypt(keyseq[key_idx], sessions_[sKey], plain_buffer,
1141 : header, footer, out, ex);
1142 0 : pOut = &out;
1143 :
1144 0 : } else if (authenticates(keyseq[key_idx])) {
1145 0 : ok = authtag(keyseq[key_idx], sessions_[sKey], plain_buffer,
1146 : header, footer, ex);
1147 :
1148 : } else {
1149 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Key transform kind unrecognized");
1150 : }
1151 :
1152 0 : if (!ok) {
1153 0 : return false; // either encrypt() or authtag() already set 'ex'
1154 : }
1155 :
1156 0 : size_t size = serialized_size(common_encoding, header);
1157 :
1158 0 : if (pOut != &plain_buffer) {
1159 0 : size += CRYPTO_CONTENT_ADDED_LENGTH;
1160 : }
1161 :
1162 0 : size += pOut->length();
1163 0 : serialized_size(common_encoding, size, footer);
1164 :
1165 0 : encoded_buffer.length(static_cast<unsigned int>(size));
1166 0 : ACE_Message_Block mb(to_mb(encoded_buffer.get_buffer()), size);
1167 0 : Serializer ser(&mb, common_encoding);
1168 0 : ser << header;
1169 :
1170 0 : if (pOut != &plain_buffer) {
1171 0 : ser << pOut->length();
1172 : }
1173 0 : ser.write_octet_array(pOut->get_buffer(), pOut->length());
1174 :
1175 0 : ser << footer;
1176 0 : return ser.good_bit();
1177 2 : }
1178 :
1179 0 : void CryptoBuiltInImpl::Session::create_key(const KeyMaterial& master)
1180 : {
1181 0 : RAND_bytes(id_, sizeof id_);
1182 0 : RAND_bytes(iv_suffix_, sizeof iv_suffix_);
1183 0 : derive_key(master);
1184 0 : counter_ = 0;
1185 0 : }
1186 :
1187 0 : void CryptoBuiltInImpl::Session::next_id(const KeyMaterial& master)
1188 : {
1189 0 : inc32(id_);
1190 0 : RAND_bytes(iv_suffix_, sizeof iv_suffix_);
1191 0 : key_.length(0);
1192 0 : derive_key(master);
1193 0 : counter_ = 0;
1194 0 : }
1195 :
1196 0 : void CryptoBuiltInImpl::Session::inc_iv()
1197 : {
1198 0 : if (inc32(iv_suffix_)) {
1199 0 : inc32(iv_suffix_ + 4);
1200 : }
1201 0 : }
1202 :
1203 0 : void CryptoBuiltInImpl::encauth_setup(const KeyMaterial& master, Session& sess,
1204 : const DDS::OctetSeq& plain,
1205 : CryptoHeader& header)
1206 : {
1207 : const unsigned int blocks =
1208 0 : (plain.length() + BLOCK_LEN_BYTES - 1) / BLOCK_LEN_BYTES;
1209 :
1210 0 : if (!sess.key_.length()) {
1211 0 : sess.create_key(master);
1212 :
1213 0 : } else if (sess.counter_ + blocks > MAX_BLOCKS_PER_SESSION) {
1214 0 : sess.next_id(master);
1215 :
1216 : } else {
1217 0 : sess.inc_iv();
1218 0 : sess.counter_ += blocks;
1219 : }
1220 :
1221 0 : std::memcpy(&header.transform_identifier.transformation_kind,
1222 0 : &master.transformation_kind, sizeof master.transformation_kind);
1223 0 : std::memcpy(&header.transform_identifier.transformation_key_id,
1224 0 : &master.sender_key_id, sizeof master.sender_key_id);
1225 0 : std::memcpy(&header.session_id, &sess.id_, sizeof sess.id_);
1226 0 : std::memcpy(&header.initialization_vector_suffix, &sess.iv_suffix_, sizeof sess.iv_suffix_);
1227 0 : }
1228 :
1229 0 : bool CryptoBuiltInImpl::encrypt(const KeyMaterial& master, Session& sess,
1230 : const DDS::OctetSeq& plain,
1231 : CryptoHeader& header, CryptoFooter& footer,
1232 : DDS::OctetSeq& out, SecurityException& ex)
1233 : {
1234 0 : if (security_debug.showkeys) {
1235 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {showkeys} CryptoBuiltInImpl::encrypt: ")
1236 : ACE_TEXT("Using this key to encrypt:\n%C"),
1237 : to_dds_string(master).c_str()));
1238 : }
1239 :
1240 0 : encauth_setup(master, sess, plain, header);
1241 : static const int IV_LEN = 12, IV_SUFFIX_IDX = 4;
1242 : unsigned char iv[IV_LEN];
1243 0 : std::memcpy(iv, &sess.id_, sizeof sess.id_);
1244 0 : std::memcpy(iv + IV_SUFFIX_IDX, &sess.iv_suffix_, sizeof sess.iv_suffix_);
1245 :
1246 0 : if (security_debug.fake_encryption) {
1247 0 : out = plain;
1248 0 : return true;
1249 : }
1250 :
1251 0 : CipherContext ctx;
1252 0 : const unsigned char* const key = sess.key_.get_buffer();
1253 0 : if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), 0, key, iv) != 1) {
1254 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_EncryptInit_ex");
1255 : }
1256 :
1257 : int len;
1258 0 : out.length(plain.length() + BLOCK_LEN_BYTES - 1);
1259 0 : unsigned char* const out_buffer = out.get_buffer();
1260 0 : if (EVP_EncryptUpdate(ctx, out_buffer, &len,
1261 0 : plain.get_buffer(), plain.length()) != 1) {
1262 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_EncryptUpdate");
1263 : }
1264 :
1265 : int padLen;
1266 0 : if (EVP_EncryptFinal_ex(ctx, out_buffer + len, &padLen) != 1) {
1267 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_EncryptFinal_ex");
1268 : }
1269 :
1270 0 : out.length(len + padLen);
1271 :
1272 0 : if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, sizeof footer.common_mac,
1273 0 : &footer.common_mac) != 1) {
1274 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_CIPHER_CTX_ctrl");
1275 : }
1276 :
1277 0 : return true;
1278 0 : }
1279 :
1280 0 : bool CryptoBuiltInImpl::authtag(const KeyMaterial& master, Session& sess,
1281 : const DDS::OctetSeq& plain,
1282 : CryptoHeader& header,
1283 : CryptoFooter& footer,
1284 : SecurityException& ex)
1285 : {
1286 0 : encauth_setup(master, sess, plain, header);
1287 : static const int IV_LEN = 12, IV_SUFFIX_IDX = 4;
1288 : unsigned char iv[IV_LEN];
1289 0 : std::memcpy(iv, &sess.id_, sizeof sess.id_);
1290 0 : std::memcpy(iv + IV_SUFFIX_IDX, &sess.iv_suffix_, sizeof sess.iv_suffix_);
1291 :
1292 0 : CipherContext ctx;
1293 0 : const unsigned char* const key = sess.key_.get_buffer();
1294 0 : if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), 0, key, iv) != 1) {
1295 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_EncryptInit_ex");
1296 : }
1297 :
1298 : int n;
1299 0 : if (EVP_EncryptUpdate(ctx, 0, &n, plain.get_buffer(), plain.length()) != 1) {
1300 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_EncryptUpdate");
1301 : }
1302 :
1303 0 : if (EVP_EncryptFinal_ex(ctx, 0, &n) != 1) {
1304 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_EncryptFinal_ex");
1305 : }
1306 :
1307 0 : if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, sizeof footer.common_mac,
1308 0 : &footer.common_mac) != 1) {
1309 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_CIPHER_CTX_ctrl");
1310 : }
1311 :
1312 0 : return true;
1313 0 : }
1314 :
1315 : namespace {
1316 : // Precondition: the bytes of 'plaintext' are a sequence of RTPS Submessages
1317 : // Returns the index of the final Submessage in the sequence, or 0 if this can't be determined.
1318 0 : unsigned int findLastSubmessage(const DDS::OctetSeq& plaintext)
1319 : {
1320 0 : RTPS::MessageParser parser(plaintext);
1321 0 : const char* const start = parser.current();
1322 :
1323 0 : while (parser.remaining() >= RTPS::SMHDR_SZ) {
1324 0 : const unsigned int sm_start = static_cast<unsigned int>(parser.current() - start);
1325 :
1326 0 : if (!parser.parseSubmessageHeader()) {
1327 0 : return 0;
1328 : }
1329 :
1330 0 : if (!parser.hasNextSubmessage()) {
1331 0 : return sm_start;
1332 : }
1333 :
1334 0 : parser.skipToNextSubmessage();
1335 : }
1336 :
1337 0 : return 0;
1338 0 : }
1339 :
1340 0 : unsigned int roundUp(unsigned int length, unsigned int alignment)
1341 : {
1342 0 : const unsigned int offset = length % alignment;
1343 0 : return length + (offset ? alignment - offset : 0);
1344 : }
1345 :
1346 : const int SEQLEN_SZ = 4;
1347 :
1348 : // Precondition: the bytes of 'original' starting at 'offset' to the end of 'original' are a valid Submessage
1349 : // If that Submessage has octetsToNextHeader == 0, returns true and makes 'modified' a copy of 'original'
1350 : // with the Submessage's octetsToNextHeader set to the actual byte count from the end of its SubmessageHeader
1351 : // to the end of 'original', rounded up to the alignment requirements (4 bytes). Otherwise returns false.
1352 0 : bool setOctetsToNextHeader(DDS::OctetSeq& modified, const DDS::OctetSeq& original, unsigned int offset = 0)
1353 : {
1354 0 : if (offset + RTPS::SMHDR_SZ >= original.length()) {
1355 0 : return false;
1356 : }
1357 :
1358 0 : const size_t origLength = original.length() - offset;
1359 0 : ACE_Message_Block mb_in(to_mb(original.get_buffer() + offset), origLength);
1360 0 : mb_in.wr_ptr(origLength);
1361 :
1362 0 : Serializer ser_in(&mb_in, common_encoding);
1363 0 : ser_in.skip(1); // submessageId
1364 :
1365 : unsigned char flags;
1366 0 : ser_in >> ACE_InputCDR::to_octet(flags);
1367 0 : const int flag_e = flags & RTPS::FLAG_E;
1368 0 : ser_in.swap_bytes(ACE_CDR_BYTE_ORDER != flag_e);
1369 :
1370 : ACE_UINT16 submessageLength;
1371 0 : ser_in >> submessageLength;
1372 0 : if (submessageLength == 0) {
1373 0 : modified = original;
1374 0 : const size_t len = roundUp(static_cast<unsigned int>(origLength - RTPS::SMHDR_SZ), RTPS::SM_ALIGN);
1375 0 : modified[offset + 2 + !flag_e] = len & 0xff;
1376 0 : modified[offset + 2 + flag_e] = (len >> 8) & 0xff;
1377 0 : return true;
1378 : }
1379 0 : return false;
1380 0 : }
1381 : }
1382 :
1383 2 : bool CryptoBuiltInImpl::encode_submessage(
1384 : DDS::OctetSeq& encoded_rtps_submessage,
1385 : const DDS::OctetSeq& plain_rtps_submessage,
1386 : NativeCryptoHandle sender_handle,
1387 : SecurityException& ex)
1388 : {
1389 2 : const KeyTable_t::const_iterator iter = keys_.find(sender_handle);
1390 2 : if (iter == keys_.end()) {
1391 2 : encoded_rtps_submessage = plain_rtps_submessage;
1392 2 : return true;
1393 : }
1394 :
1395 0 : const KeySeq& keyseq = iter->second;
1396 0 : if (!keyseq.length()) {
1397 0 : encoded_rtps_submessage = plain_rtps_submessage;
1398 0 : return true;
1399 : }
1400 :
1401 : bool ok;
1402 : CryptoHeader header;
1403 0 : CryptoFooter footer;
1404 0 : DDS::OctetSeq out;
1405 0 : const DDS::OctetSeq* pOut = &plain_rtps_submessage;
1406 0 : const KeyId_t sKey = std::make_pair(sender_handle, submessage_key_index);
1407 0 : bool authOnly = false;
1408 :
1409 0 : if (encrypts(keyseq[submessage_key_index])) {
1410 0 : ok = encrypt(keyseq[submessage_key_index], sessions_[sKey], plain_rtps_submessage,
1411 : header, footer, out, ex);
1412 0 : pOut = &out;
1413 :
1414 0 : } else if (authenticates(keyseq[submessage_key_index])) {
1415 : // the original submessage may have octetsToNextHeader = 0 which isn't
1416 : // legal when appending SEC_POSTFIX, patch in the actual submsg length
1417 0 : if (setOctetsToNextHeader(out, plain_rtps_submessage)) {
1418 0 : pOut = &out;
1419 : }
1420 0 : ok = authtag(keyseq[submessage_key_index], sessions_[sKey], *pOut,
1421 : header, footer, ex);
1422 0 : authOnly = true;
1423 :
1424 : } else {
1425 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Key transform kind unrecognized");
1426 : }
1427 :
1428 0 : if (!ok) {
1429 0 : return false; // either encrypt() or authtag() already set 'ex'
1430 : }
1431 :
1432 0 : size_t size = 0;
1433 :
1434 0 : size += RTPS::SMHDR_SZ; // prefix submessage header
1435 0 : serialized_size(common_encoding, size, header);
1436 0 : const ACE_UINT16 hdrLen = static_cast<ACE_UINT16>(size - RTPS::SMHDR_SZ);
1437 :
1438 0 : if (!authOnly) {
1439 0 : size += RTPS::SMHDR_SZ + SEQLEN_SZ;
1440 : }
1441 :
1442 0 : size += pOut->length(); // submessage inside wrapper
1443 0 : align(size, RTPS::SM_ALIGN);
1444 :
1445 0 : size += RTPS::SMHDR_SZ; // postfix submessage header
1446 0 : const size_t preFooter = size;
1447 0 : serialized_size(common_encoding, size, footer);
1448 :
1449 0 : encoded_rtps_submessage.length(static_cast<unsigned int>(size));
1450 0 : ACE_Message_Block mb(to_mb(encoded_rtps_submessage.get_buffer()), size);
1451 0 : Serializer ser(&mb, common_encoding);
1452 0 : RTPS::SubmessageHeader smHdr = {RTPS::SEC_PREFIX, 0, hdrLen};
1453 0 : ser << smHdr;
1454 0 : ser << header;
1455 :
1456 0 : if (!authOnly) {
1457 0 : smHdr.submessageId = RTPS::SEC_BODY;
1458 0 : smHdr.submessageLength = static_cast<ACE_UINT16>(roundUp(SEQLEN_SZ + pOut->length(), RTPS::SM_ALIGN));
1459 0 : ser << smHdr;
1460 0 : ser << pOut->length();
1461 : }
1462 :
1463 0 : ser.write_octet_array(pOut->get_buffer(), pOut->length());
1464 0 : ser.align_w(RTPS::SM_ALIGN);
1465 :
1466 0 : smHdr.submessageId = RTPS::SEC_POSTFIX;
1467 0 : smHdr.submessageLength = static_cast<ACE_UINT16>(size - preFooter);
1468 0 : ser << smHdr;
1469 0 : ser << footer;
1470 :
1471 0 : return ser.good_bit();
1472 0 : }
1473 :
1474 23 : bool CryptoBuiltInImpl::encode_datawriter_submessage(
1475 : DDS::OctetSeq& encoded_rtps_submessage,
1476 : const DDS::OctetSeq& plain_rtps_submessage,
1477 : DatawriterCryptoHandle sending_datawriter_crypto,
1478 : const DatareaderCryptoHandleSeq& receiving_datareader_crypto_list,
1479 : CORBA::Long& receiving_datareader_crypto_list_index,
1480 : SecurityException& ex)
1481 : {
1482 23 : if (DDS::HANDLE_NIL == sending_datawriter_crypto) {
1483 1 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid DataWriter handle");
1484 : }
1485 :
1486 22 : if (receiving_datareader_crypto_list_index < 0) {
1487 1 : return CommonUtilities::set_security_error(ex, -1, 0, "Negative list index");
1488 : }
1489 :
1490 21 : const int len = static_cast<int>(receiving_datareader_crypto_list.length());
1491 : // NOTE: as an extension to the spec, this plugin allows an empty list in the
1492 : // case where the writer is sending to all associated readers.
1493 21 : if (len && receiving_datareader_crypto_list_index >= len) {
1494 1 : return CommonUtilities::set_security_error(ex, -1, 0, "List index too large");
1495 : }
1496 :
1497 165 : for (unsigned int i = 0; i < receiving_datareader_crypto_list.length(); ++i) {
1498 155 : if (receiving_datareader_crypto_list[i] == DDS::HANDLE_NIL) {
1499 10 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid DataReader handle in list");
1500 : }
1501 : }
1502 :
1503 10 : NativeCryptoHandle encode_handle = sending_datawriter_crypto;
1504 10 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
1505 10 : const EncryptOptions_t::const_iterator eo_iter = encrypt_options_.find(encode_handle);
1506 10 : if (eo_iter == encrypt_options_.end()) {
1507 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Datawriter handle lacks encrypt options");
1508 : }
1509 :
1510 10 : if (!eo_iter->second.submessage_) {
1511 10 : encoded_rtps_submessage = plain_rtps_submessage;
1512 10 : receiving_datareader_crypto_list_index = len;
1513 10 : return true;
1514 : }
1515 :
1516 0 : if (receiving_datareader_crypto_list.length() == 1) {
1517 0 : const KeyTable_t::const_iterator iter = keys_.find(encode_handle);
1518 0 : if (iter != keys_.end()) {
1519 0 : const KeySeq& dw_keys = iter->second;
1520 0 : if (dw_keys.length() == 1 && is_volatile_placeholder(dw_keys[0])) {
1521 0 : encode_handle = receiving_datareader_crypto_list[0];
1522 : }
1523 : }
1524 : }
1525 :
1526 0 : const bool ok = encode_submessage(encoded_rtps_submessage,
1527 : plain_rtps_submessage, encode_handle, ex);
1528 0 : if (ok) {
1529 0 : receiving_datareader_crypto_list_index = len;
1530 : }
1531 0 : return ok;
1532 10 : }
1533 :
1534 3 : bool CryptoBuiltInImpl::encode_datareader_submessage(
1535 : DDS::OctetSeq& encoded_rtps_submessage,
1536 : const DDS::OctetSeq& plain_rtps_submessage,
1537 : DatareaderCryptoHandle sending_datareader_crypto,
1538 : const DatawriterCryptoHandleSeq& receiving_datawriter_crypto_list,
1539 : SecurityException& ex)
1540 : {
1541 3 : if (DDS::HANDLE_NIL == sending_datareader_crypto) {
1542 1 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid DataReader handle");
1543 : }
1544 :
1545 8 : for (unsigned int i = 0; i < receiving_datawriter_crypto_list.length(); ++i) {
1546 6 : if (receiving_datawriter_crypto_list[i] == DDS::HANDLE_NIL) {
1547 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid DataWriter handle in list");
1548 : }
1549 : }
1550 :
1551 2 : NativeCryptoHandle encode_handle = sending_datareader_crypto;
1552 2 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
1553 2 : if (receiving_datawriter_crypto_list.length() == 1) {
1554 1 : const KeyTable_t::const_iterator iter = keys_.find(encode_handle);
1555 1 : if (iter != keys_.end()) {
1556 0 : const KeySeq& dr_keys = iter->second;
1557 0 : if (dr_keys.length() == 1 && is_volatile_placeholder(dr_keys[0])) {
1558 0 : encode_handle = receiving_datawriter_crypto_list[0];
1559 : }
1560 : }
1561 : }
1562 :
1563 2 : return encode_submessage(encoded_rtps_submessage, plain_rtps_submessage,
1564 2 : encode_handle, ex);
1565 2 : }
1566 :
1567 3 : bool CryptoBuiltInImpl::encode_rtps_message(
1568 : DDS::OctetSeq& encoded_rtps_message,
1569 : const DDS::OctetSeq& plain_rtps_message,
1570 : ParticipantCryptoHandle sending_participant_crypto,
1571 : const ParticipantCryptoHandleSeq& receiving_participant_crypto_list,
1572 : CORBA::Long& receiving_participant_crypto_list_index,
1573 : SecurityException& ex)
1574 : {
1575 3 : receiving_participant_crypto_list_index = receiving_participant_crypto_list.length();
1576 3 : if (DDS::HANDLE_NIL == sending_participant_crypto) {
1577 : // DDS-Security v1.1 8.5.1.9.4
1578 : // This operation may optionally not perform any transformation of the input RTPS message.
1579 : // In this case, the operation shall return false but not set the exception object.
1580 1 : return false;
1581 : }
1582 :
1583 2 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
1584 2 : const KeyTable_t::const_iterator iter = keys_.find(sending_participant_crypto);
1585 2 : if (iter == keys_.end()) {
1586 2 : return CommonUtilities::set_security_error(ex, -1, 0, "No entry for sending_participant_crypto");
1587 : }
1588 :
1589 0 : const KeySeq& keyseq = iter->second;
1590 0 : if (!keyseq.length()) {
1591 0 : return CommonUtilities::set_security_error(ex, -1, 0, "No key for sending_participant_crypto");
1592 : }
1593 :
1594 : // The input with its RTPS Header changed to an InfoSrc submessage acts as plaintext for encrypt/authenticate
1595 0 : DDS::OctetSeq transformed(plain_rtps_message.length() + RTPS::SMHDR_SZ);
1596 0 : transformed.length(transformed.maximum());
1597 0 : transformed[0] = RTPS::INFO_SRC;
1598 0 : transformed[1] = 0; // flags: big-endian
1599 0 : transformed[2] = 0; // high byte of octetsToNextHeader
1600 0 : transformed[3] = RTPS::INFO_SRC_SZ;
1601 0 : std::memcpy(transformed.get_buffer() + RTPS::SMHDR_SZ, plain_rtps_message.get_buffer(), plain_rtps_message.length());
1602 :
1603 0 : bool ok, addSecBody = false;
1604 : CryptoHeader cryptoHdr;
1605 0 : CryptoFooter cryptoFooter;
1606 0 : DDS::OctetSeq out;
1607 0 : const DDS::OctetSeq* pOut = &transformed;
1608 0 : const KeyMaterial& key = keyseq[0];
1609 0 : const KeyId_t sKey = std::make_pair(sending_participant_crypto, 0);
1610 :
1611 0 : if (encrypts(key)) {
1612 0 : ok = encrypt(key, sessions_[sKey], transformed, cryptoHdr, cryptoFooter, out, ex);
1613 0 : pOut = &out;
1614 0 : addSecBody = true;
1615 :
1616 0 : } else if (authenticates(key)) {
1617 : // the original message's last submsg may have octetsToNextHeader = 0 which
1618 : // isn't valid when appending SEC_POSTFIX, patch in the actual submsg length
1619 0 : const unsigned int offsetFinal = findLastSubmessage(transformed);
1620 0 : if (offsetFinal && setOctetsToNextHeader(out, transformed, offsetFinal)) {
1621 0 : pOut = &out;
1622 : }
1623 0 : ok = authtag(key, sessions_[sKey], *pOut, cryptoHdr, cryptoFooter, ex);
1624 :
1625 : } else {
1626 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Key transform kind unrecognized");
1627 : }
1628 :
1629 0 : if (!ok) {
1630 0 : return false; // either encrypt() or authtag() already set 'ex'
1631 : }
1632 :
1633 0 : size_t size = RTPS::RTPSHDR_SZ + RTPS::SMHDR_SZ; // RTPS Header, SRTPS Prefix
1634 0 : serialized_size(common_encoding, size, cryptoHdr);
1635 0 : const ACE_UINT16 cryptoHdrLen =
1636 0 : static_cast<ACE_UINT16>(size - RTPS::RTPSHDR_SZ - RTPS::SMHDR_SZ);
1637 :
1638 0 : if (addSecBody) {
1639 0 : size += RTPS::SMHDR_SZ + SEQLEN_SZ;
1640 : }
1641 :
1642 0 : size += pOut->length();
1643 0 : align(size, RTPS::SM_ALIGN);
1644 :
1645 0 : size += RTPS::SMHDR_SZ; // SRTPS Postfix
1646 0 : serialized_size(common_encoding, size, cryptoFooter);
1647 :
1648 0 : encoded_rtps_message.length(static_cast<unsigned int>(size));
1649 0 : ACE_Message_Block mb(to_mb(encoded_rtps_message.get_buffer()), size);
1650 0 : Serializer ser(&mb, common_encoding);
1651 :
1652 0 : ser.write_octet_array(plain_rtps_message.get_buffer(), RTPS::RTPSHDR_SZ);
1653 :
1654 0 : RTPS::SubmessageHeader smHdr = {RTPS::SRTPS_PREFIX, 0, cryptoHdrLen};
1655 0 : ser << smHdr;
1656 0 : ser << cryptoHdr;
1657 :
1658 0 : if (addSecBody) {
1659 0 : smHdr.submessageId = RTPS::SEC_BODY;
1660 0 : smHdr.submessageLength = static_cast<ACE_UINT16>(roundUp(SEQLEN_SZ + pOut->length(), RTPS::SM_ALIGN));
1661 0 : ser << smHdr;
1662 0 : ser << pOut->length();
1663 : }
1664 :
1665 0 : ser.write_octet_array(pOut->get_buffer(), pOut->length());
1666 0 : ser.align_w(RTPS::SM_ALIGN);
1667 :
1668 0 : smHdr.submessageId = RTPS::SRTPS_POSTFIX;
1669 0 : smHdr.submessageLength = 0; // final submessage doesn't need a length
1670 0 : ser << smHdr;
1671 0 : ser << cryptoFooter;
1672 :
1673 0 : return ser.good_bit();
1674 2 : }
1675 :
1676 : namespace {
1677 0 : bool matches(const KeyMaterial_AES_GCM_GMAC& k, const CryptoHeader& h)
1678 : {
1679 0 : return 0 == std::memcmp(k.transformation_kind,
1680 0 : h.transform_identifier.transformation_kind,
1681 : sizeof(CryptoTransformKind))
1682 0 : && 0 == std::memcmp(k.sender_key_id,
1683 0 : h.transform_identifier.transformation_key_id,
1684 0 : sizeof(CryptoTransformKeyId));
1685 : }
1686 : }
1687 :
1688 3 : bool CryptoBuiltInImpl::preprocess_secure_submsg(
1689 : DatawriterCryptoHandle& datawriter_crypto,
1690 : DatareaderCryptoHandle& datareader_crypto,
1691 : SecureSubmessageCategory_t& secure_submessage_category,
1692 : const DDS::OctetSeq& encoded_rtps_submessage,
1693 : ParticipantCryptoHandle,
1694 : ParticipantCryptoHandle sending_participant_crypto,
1695 : SecurityException& ex)
1696 : {
1697 3 : if (DDS::HANDLE_NIL == sending_participant_crypto) {
1698 2 : CommonUtilities::set_security_error(ex, -1, 0, "Invalid Sending Participant");
1699 2 : return false;
1700 : }
1701 :
1702 : ACE_Message_Block mb_in(to_mb(encoded_rtps_submessage.get_buffer()),
1703 1 : encoded_rtps_submessage.length());
1704 1 : mb_in.wr_ptr(encoded_rtps_submessage.length());
1705 1 : Serializer de_ser(&mb_in, common_encoding);
1706 1 : CryptoHeader ch = CryptoHeader();
1707 1 : if (!(de_ser.skip(RTPS::SMHDR_SZ) && (de_ser >> ch))) {
1708 1 : ACE_ERROR((LM_ERROR,
1709 : "(%P|%t) CryptoBuiltInImpl::preprocess_secure_submsg: "
1710 : "Could not deserializer CyptoHeader\n"));
1711 : }
1712 :
1713 1 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
1714 : typedef std::multimap<ParticipantCryptoHandle, EntityInfo>::iterator iter_t;
1715 : const std::pair<iter_t, iter_t> iters =
1716 1 : participant_to_entity_.equal_range(sending_participant_crypto);
1717 1 : if (security_debug.chlookup) {
1718 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {chlookup} CryptoBuiltInImpl::preprocess_secure_submsg: ")
1719 : ACE_TEXT("Looking for CH that matches transformation id:\n%C"),
1720 : to_dds_string(ch.transform_identifier).c_str()));
1721 : }
1722 1 : for (iter_t iter = iters.first; iter != iters.second; ++iter) {
1723 0 : const NativeCryptoHandle sending_entity_candidate = iter->second.handle_;
1724 0 : const KeyTable_t::const_iterator kiter = keys_.find(sending_entity_candidate);
1725 0 : if (security_debug.chlookup) {
1726 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {chlookup} CryptoBuiltInImpl::preprocess_secure_submsg: ")
1727 : ACE_TEXT(" Looking at CH %u, has keys: %C\n"),
1728 : sending_entity_candidate, kiter == keys_.end() ? "false" : "true"));
1729 : }
1730 0 : if (kiter != keys_.end()) {
1731 0 : const KeySeq& keyseq = kiter->second;
1732 0 : const unsigned keycount = keyseq.length();
1733 0 : if (security_debug.chlookup) {
1734 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {chlookup} CryptoBuiltInImpl::preprocess_secure_submsg: ")
1735 : ACE_TEXT(" Number of keys: %u\n"), keycount));
1736 : }
1737 0 : for (unsigned int i = 0; i < keycount; ++i) {
1738 0 : if (security_debug.chlookup) {
1739 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {chlookup} CryptoBuiltInImpl::preprocess_secure_submsg: ")
1740 : ACE_TEXT(" Key: %C\n"),
1741 : (OPENDDS_STRING(ctk_to_dds_string(keyseq[i].transformation_kind)) + ", " +
1742 : ctki_to_dds_string(keyseq[i].sender_key_id)).c_str()));
1743 : }
1744 0 : if (matches(keyseq[i], ch)) {
1745 0 : char chtype = '\0';
1746 0 : secure_submessage_category = iter->second.category_;
1747 0 : switch (secure_submessage_category) {
1748 0 : case DATAWRITER_SUBMESSAGE:
1749 0 : datawriter_crypto = iter->second.handle_;
1750 0 : chtype = 'W';
1751 0 : break;
1752 0 : case DATAREADER_SUBMESSAGE:
1753 0 : datareader_crypto = iter->second.handle_;
1754 0 : chtype = 'R';
1755 0 : break;
1756 0 : default:
1757 0 : break;
1758 : }
1759 0 : if (security_debug.chlookup && chtype) {
1760 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {chlookup} CryptoBuiltInImpl::preprocess_secure_submsg: ")
1761 : ACE_TEXT("D%cCH Found!\n"), chtype));
1762 : }
1763 0 : return true;
1764 : }
1765 : }
1766 : }
1767 : }
1768 1 : CommonUtilities::set_security_error(ex, -2, 1, "Crypto Key not registered",
1769 : ch.transform_identifier.transformation_kind,
1770 : ch.transform_identifier.transformation_key_id);
1771 1 : return false;
1772 1 : }
1773 :
1774 : KeyOctetSeq
1775 0 : CryptoBuiltInImpl::Session::get_key(const KeyMaterial& master,
1776 : const CryptoHeader& header)
1777 : {
1778 0 : if (key_.length() && 0 == std::memcmp(&id_, &header.session_id, sizeof id_)) {
1779 0 : return key_;
1780 : }
1781 0 : std::memcpy(&id_, &header.session_id, sizeof id_);
1782 0 : key_.length(0);
1783 0 : derive_key(master);
1784 0 : return key_;
1785 : }
1786 :
1787 0 : void CryptoBuiltInImpl::Session::derive_key(const KeyMaterial& master)
1788 : {
1789 0 : PrivateKey pkey(master.master_sender_key);
1790 0 : DigestContext ctx;
1791 0 : const EVP_MD* md = EVP_get_digestbyname("SHA256");
1792 :
1793 0 : if (EVP_DigestInit_ex(ctx, md, 0) < 1) {
1794 0 : return;
1795 : }
1796 :
1797 0 : if (EVP_DigestSignInit(ctx, 0, md, 0, pkey) < 1) {
1798 0 : return;
1799 : }
1800 :
1801 : static const char cookie[] = "SessionKey"; // DDSSEC12-53: NUL excluded
1802 0 : if (EVP_DigestSignUpdate(ctx, cookie, (sizeof cookie) - 1) < 1) {
1803 0 : return;
1804 : }
1805 :
1806 0 : const KeyOctetSeq& salt = master.master_salt;
1807 0 : if (EVP_DigestSignUpdate(ctx, salt.get_buffer(), salt.length()) < 1) {
1808 0 : return;
1809 : }
1810 :
1811 0 : if (EVP_DigestSignUpdate(ctx, id_, sizeof id_) < 1) {
1812 0 : return;
1813 : }
1814 :
1815 0 : size_t req = 0;
1816 0 : if (EVP_DigestSignFinal(ctx, 0, &req) < 1) {
1817 0 : return;
1818 : }
1819 :
1820 0 : key_.length(static_cast<unsigned int>(req));
1821 0 : if (EVP_DigestSignFinal(ctx, key_.get_buffer(), &req) < 1) {
1822 0 : key_.length(0);
1823 : }
1824 0 : }
1825 :
1826 0 : bool CryptoBuiltInImpl::decrypt(const KeyMaterial& master, Session& sess,
1827 : const char* ciphertext, unsigned int n,
1828 : const CryptoHeader& header,
1829 : const CryptoFooter& footer, DDS::OctetSeq& out,
1830 : SecurityException& ex)
1831 :
1832 : {
1833 0 : if (security_debug.showkeys) {
1834 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {showkeys} CryptoBuiltInImpl::decrypt ")
1835 : ACE_TEXT("Using this key to decrypt:\n%C"),
1836 : to_dds_string(master).c_str()));
1837 : }
1838 :
1839 0 : const KeyOctetSeq sess_key = sess.get_key(master, header);
1840 0 : if (!sess_key.length()) {
1841 0 : return CommonUtilities::set_security_error(ex, -1, 0, "no session key");
1842 : }
1843 :
1844 0 : if (master.transformation_kind[TransformKindIndex] !=
1845 : CRYPTO_TRANSFORMATION_KIND_AES256_GCM) {
1846 0 : ACE_ERROR((LM_ERROR, "(%P|%t) CryptoBuiltInImpl::decrypt - ERROR "
1847 : "unsupported transformation kind %d\n",
1848 : master.transformation_kind[TransformKindIndex]));
1849 0 : return CommonUtilities::set_security_error(ex, -1, 0, "unsupported transformation kind");
1850 : }
1851 :
1852 0 : if (security_debug.fake_encryption) {
1853 0 : out.length(n);
1854 0 : std::memcpy(out.get_buffer(), ciphertext, n);
1855 0 : return true;
1856 : }
1857 :
1858 0 : CipherContext ctx;
1859 : // session_id is start of IV contiguous bytes
1860 0 : if (EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), 0, sess_key.get_buffer(),
1861 0 : header.session_id) != 1) {
1862 0 : ACE_ERROR((LM_ERROR, "(%P|%t) CryptoBuiltInImpl::decrypt - ERROR "
1863 : "EVP_DecryptInit_ex %Ld\n", ERR_peek_last_error()));
1864 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_DecryptInit_ex");
1865 : }
1866 :
1867 0 : out.length(n + KEY_LEN_BYTES);
1868 0 : unsigned char* const out_buffer = out.get_buffer();
1869 : int len;
1870 0 : if (EVP_DecryptUpdate(ctx, out_buffer, &len,
1871 : reinterpret_cast<const unsigned char*>(ciphertext), n)
1872 0 : != 1) {
1873 0 : ACE_ERROR((LM_ERROR, "(%P|%t) CryptoBuiltInImpl::decrypt - ERROR "
1874 : "EVP_DecryptUpdate %Ld\n", ERR_peek_last_error()));
1875 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_DecryptUpdate");
1876 : }
1877 :
1878 0 : void* tag = const_cast<void*>(static_cast<const void*>(footer.common_mac));
1879 0 : if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag)) {
1880 0 : ACE_ERROR((LM_ERROR, "(%P|%t) CryptoBuiltInImpl::decrypt - ERROR "
1881 : "EVP_CIPHER_CTX_ctrl %Ld\n", ERR_peek_last_error()));
1882 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_CIPHER_CTX_ctrl");
1883 : }
1884 :
1885 : int len2;
1886 0 : if (EVP_DecryptFinal_ex(ctx, out_buffer + len, &len2) == 1) {
1887 0 : out.length(len + len2);
1888 0 : return true;
1889 : }
1890 0 : ACE_ERROR((LM_ERROR, "(%P|%t) CryptoBuiltInImpl::decrypt - ERROR "
1891 : "EVP_DecryptFinal_ex %Ld\n", ERR_peek_last_error()));
1892 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_DecryptFinal_ex");
1893 0 : }
1894 :
1895 0 : bool CryptoBuiltInImpl::verify(const KeyMaterial& master, Session& sess,
1896 : const char* in, unsigned int n,
1897 : const CryptoHeader& header,
1898 : const CryptoFooter& footer, DDS::OctetSeq& out,
1899 : SecurityException& ex)
1900 :
1901 : {
1902 0 : const KeyOctetSeq sess_key = sess.get_key(master, header);
1903 0 : if (!sess_key.length()) {
1904 0 : return CommonUtilities::set_security_error(ex, -1, 0, "no session key");
1905 : }
1906 :
1907 0 : if (master.transformation_kind[TransformKindIndex] !=
1908 : CRYPTO_TRANSFORMATION_KIND_AES256_GMAC) {
1909 0 : ACE_ERROR((LM_ERROR, "(%P|%t) CryptoBuiltInImpl::verify - ERROR "
1910 : "unsupported transformation kind %d\n",
1911 : master.transformation_kind[TransformKindIndex]));
1912 0 : return CommonUtilities::set_security_error(ex, -1, 0, "unsupported transformation kind");
1913 : }
1914 :
1915 0 : CipherContext ctx;
1916 : // session_id is start of IV contiguous bytes
1917 0 : if (EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), 0, sess_key.get_buffer(),
1918 0 : header.session_id) != 1) {
1919 0 : ACE_ERROR((LM_ERROR, "(%P|%t) CryptoBuiltInImpl::verify - ERROR "
1920 : "EVP_DecryptInit_ex %Ld\n", ERR_peek_last_error()));
1921 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_DecryptInit_ex");
1922 : }
1923 :
1924 : int len;
1925 0 : if (EVP_DecryptUpdate(ctx, 0, &len,
1926 0 : reinterpret_cast<const unsigned char*>(in), n) != 1) {
1927 0 : ACE_ERROR((LM_ERROR, "(%P|%t) CryptoBuiltInImpl::verify - ERROR "
1928 : "EVP_DecryptUpdate %Ld\n", ERR_peek_last_error()));
1929 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_DecryptUpdate");
1930 : }
1931 :
1932 0 : void* tag = const_cast<void*>(static_cast<const void*>(footer.common_mac));
1933 0 : if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag)) {
1934 0 : ACE_ERROR((LM_ERROR, "(%P|%t) CryptoBuiltInImpl::verify - ERROR "
1935 : "EVP_CIPHER_CTX_ctrl %Ld\n", ERR_peek_last_error()));
1936 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_CIPHER_CTX_ctrl");
1937 : }
1938 :
1939 : int len2;
1940 0 : if (EVP_DecryptFinal_ex(ctx, 0, &len2) == 1) {
1941 0 : out.length(n);
1942 0 : std::memcpy(out.get_buffer(), in, n);
1943 0 : return true;
1944 : }
1945 0 : ACE_ERROR((LM_ERROR, "(%P|%t) CryptoBuiltInImpl::verify - ERROR "
1946 : "EVP_DecryptFinal_ex %Ld\n", ERR_peek_last_error()));
1947 0 : return CommonUtilities::set_security_error(ex, -1, 0, "EVP_DecryptFinal_ex");
1948 0 : }
1949 :
1950 3 : bool CryptoBuiltInImpl::decode_rtps_message(
1951 : DDS::OctetSeq& plain_buffer,
1952 : const DDS::OctetSeq& encoded_buffer,
1953 : ParticipantCryptoHandle receiving_participant_crypto,
1954 : ParticipantCryptoHandle sending_participant_crypto,
1955 : SecurityException& ex)
1956 : {
1957 3 : if (DDS::HANDLE_NIL == receiving_participant_crypto) {
1958 2 : return CommonUtilities::set_security_error(ex, -1, 0, "No Receiving Participant handle");
1959 : }
1960 1 : if (DDS::HANDLE_NIL == sending_participant_crypto) {
1961 1 : return CommonUtilities::set_security_error(ex, -1, 1, "No Sending Participant handle");
1962 : }
1963 :
1964 0 : RTPS::MessageParser parser(encoded_buffer);
1965 :
1966 0 : if (!parser.parseHeader()) {
1967 0 : return CommonUtilities::set_security_error(ex, -2, 0, "Failed to deserialize Header");
1968 : }
1969 :
1970 0 : CryptoHeader ch = CryptoHeader();
1971 0 : CryptoFooter cf;
1972 0 : bool haveCryptoHeader = false, haveCryptoFooter = false;
1973 0 : const char* afterSrtpsPrefix = 0;
1974 : unsigned int sizeOfAuthenticated, sizeOfEncrypted;
1975 0 : const char* encrypted = 0;
1976 :
1977 0 : for (int i = 0; parser.remaining(); ++i) {
1978 0 : if (parser.remaining() < RTPS::SMHDR_SZ || !parser.parseSubmessageHeader()) {
1979 0 : return CommonUtilities::set_security_error(ex, -3, i, "Failed to deserialize SubmessageHeader");
1980 : }
1981 :
1982 0 : parser.serializer().endianness(ENDIAN_BIG);
1983 0 : const int type = parser.submessageHeader().submessageId;
1984 :
1985 0 : if (i == 0 && type == RTPS::SRTPS_PREFIX) {
1986 0 : if (!(parser >> ch)) {
1987 0 : return CommonUtilities::set_security_error(ex, -4, i, "Failed to deserialize CryptoHeader");
1988 : }
1989 0 : haveCryptoHeader = true;
1990 0 : if (!parser.skipToNextSubmessage()) {
1991 0 : return CommonUtilities::set_security_error(ex, -5, i, "Failed to find submessage after SRTPS_PREFIX");
1992 : }
1993 0 : afterSrtpsPrefix = parser.current();
1994 :
1995 0 : } else if (haveCryptoHeader && type == RTPS::SEC_BODY) {
1996 0 : if (!(parser >> sizeOfEncrypted)) {
1997 0 : return CommonUtilities::set_security_error(ex, -13, i, "Failed to deserialize CryptoContent length");
1998 : }
1999 0 : const unsigned short sz =
2000 : static_cast<unsigned short>(DCPS::uint32_cdr_size);
2001 0 : if (sizeOfEncrypted + sz > parser.submessageHeader().submessageLength) {
2002 0 : return CommonUtilities::set_security_error(ex, -14, i, "CryptoContent length out of bounds");
2003 : }
2004 0 : encrypted = parser.current();
2005 0 : if (!parser.skipToNextSubmessage()) {
2006 0 : return CommonUtilities::set_security_error(ex, -15, i, "Failed to find submessage after SEC_BODY");
2007 : }
2008 :
2009 0 : } else if (haveCryptoHeader && type == RTPS::SRTPS_POSTFIX) {
2010 0 : sizeOfAuthenticated = static_cast<unsigned int>(parser.current() - afterSrtpsPrefix - RTPS::SMHDR_SZ);
2011 0 : if (!(parser >> cf)) {
2012 0 : return CommonUtilities::set_security_error(ex, -7, i, "Failed to deserialize CryptoFooter");
2013 : }
2014 0 : if (parser.hasNextSubmessage()) {
2015 0 : return CommonUtilities::set_security_error(ex, -8, i, "SRTPS_POSTFIX was not the final submessage");
2016 : }
2017 0 : haveCryptoFooter = true;
2018 0 : break;
2019 :
2020 : } else {
2021 0 : if (parser.hasNextSubmessage()) {
2022 0 : if (!parser.skipToNextSubmessage()) {
2023 0 : return CommonUtilities::set_security_error(ex, -6, i, "Failed to find next submessage");
2024 : }
2025 : } else {
2026 0 : break;
2027 : }
2028 : }
2029 : }
2030 :
2031 0 : if (!haveCryptoHeader || !haveCryptoFooter) {
2032 0 : return CommonUtilities::set_security_error(ex, -9, 0, "Failed to find SRTPS_PREFIX/POSTFIX wrapper");
2033 : }
2034 :
2035 0 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
2036 0 : const KeyTable_t::const_iterator iter = keys_.find(sending_participant_crypto);
2037 0 : if (iter == keys_.end()) {
2038 0 : return CommonUtilities::set_security_error(ex, -1, 2, "No key for Sending Participant handle");
2039 : }
2040 0 : const KeySeq& keyseq = iter->second;
2041 0 : bool foundKey = false;
2042 0 : DDS::OctetSeq transformed;
2043 0 : for (unsigned int i = 0; !foundKey && i < keyseq.length(); ++i) {
2044 0 : if (matches(keyseq[i], ch)) {
2045 0 : const KeyId_t sKey = std::make_pair(sending_participant_crypto, i);
2046 :
2047 0 : if (encrypts(keyseq[i])) {
2048 0 : if (!encrypted) {
2049 0 : return CommonUtilities::set_security_error(ex, -15, 0, "Failed to find SEC_BODY submessage");
2050 : }
2051 0 : foundKey = true;
2052 0 : if (!decrypt(keyseq[i], sessions_[sKey], encrypted, sizeOfEncrypted,
2053 : ch, cf, transformed, ex)) {
2054 0 : return false;
2055 : }
2056 :
2057 0 : } else if (authenticates(keyseq[i])) {
2058 0 : foundKey = true;
2059 0 : if (!verify(keyseq[i], sessions_[sKey], afterSrtpsPrefix, sizeOfAuthenticated,
2060 : ch, cf, transformed, ex)) {
2061 0 : return false;
2062 : }
2063 :
2064 : } else {
2065 0 : return CommonUtilities::set_security_error(ex, -10, 2, "Key transform kind unrecognized");
2066 : }
2067 : }
2068 : }
2069 :
2070 0 : if (!foundKey) {
2071 0 : return CommonUtilities::set_security_error(ex, OPENDDS_EXCEPTION_CODE_NO_KEY,
2072 0 : OPENDDS_EXCEPTION_MINOR_CODE_NO_KEY, "Crypto Key not found");
2073 : }
2074 :
2075 0 : if (transformed.length() < RTPS::SMHDR_SZ + RTPS::INFO_SRC_SZ
2076 0 : || transformed[0] != RTPS::INFO_SRC) {
2077 0 : return CommonUtilities::set_security_error(ex, -11, 0, "Plaintext doesn't start with INFO_SRC");
2078 : }
2079 :
2080 : static const int GuidPrefixOffset = 8; // "RTPS", Version(2), Vendor(2)
2081 0 : if (std::memcmp(transformed.get_buffer() + RTPS::SMHDR_SZ + GuidPrefixOffset,
2082 0 : encoded_buffer.get_buffer() + GuidPrefixOffset,
2083 : sizeof(DCPS::GuidPrefix_t))) {
2084 0 : return CommonUtilities::set_security_error(ex, -12, 0, "Header GUID Prefix doesn't match INFO_SRC");
2085 : }
2086 :
2087 0 : plain_buffer.length(transformed.length() - RTPS::SMHDR_SZ);
2088 0 : std::memcpy(plain_buffer.get_buffer(), RTPS::PROTOCOL_RTPS, sizeof RTPS::PROTOCOL_RTPS);
2089 0 : std::memcpy(plain_buffer.get_buffer() + sizeof RTPS::PROTOCOL_RTPS,
2090 0 : transformed.get_buffer() + RTPS::SMHDR_SZ + sizeof RTPS::PROTOCOL_RTPS,
2091 0 : plain_buffer.length() - sizeof RTPS::PROTOCOL_RTPS);
2092 0 : return true;
2093 0 : }
2094 :
2095 0 : bool CryptoBuiltInImpl::decode_submessage(
2096 : DDS::OctetSeq& plain_rtps_submessage,
2097 : const DDS::OctetSeq& encoded_rtps_submessage,
2098 : NativeCryptoHandle sender_handle,
2099 : SecurityException& ex)
2100 : {
2101 : ACE_Message_Block mb_in(to_mb(encoded_rtps_submessage.get_buffer()),
2102 0 : encoded_rtps_submessage.length());
2103 0 : mb_in.wr_ptr(encoded_rtps_submessage.length());
2104 0 : Serializer de_ser(&mb_in, common_encoding);
2105 : ACE_CDR::Octet type, flags;
2106 : // SEC_PREFIX
2107 0 : de_ser >> ACE_InputCDR::to_octet(type);
2108 0 : de_ser >> ACE_InputCDR::to_octet(flags);
2109 0 : de_ser.swap_bytes((flags & RTPS::FLAG_E) != ACE_CDR_BYTE_ORDER);
2110 : ACE_CDR::UShort octetsToNext;
2111 0 : de_ser >> octetsToNext;
2112 0 : CryptoHeader ch = CryptoHeader();
2113 0 : de_ser.endianness(ENDIAN_BIG);
2114 0 : de_ser >> ch;
2115 0 : de_ser.skip(octetsToNext - CRYPTO_HEADER_LENGTH);
2116 0 : if (!de_ser.good_bit()) {
2117 0 : ACE_ERROR((LM_ERROR,
2118 : "(%P|%t) ERROR: CryptoBuiltInImpl::decode_submessage: "
2119 : "Failed to deserialize SEC_PREFIX\n"));
2120 0 : return false;
2121 : }
2122 :
2123 : // Next submessage, SEC_BODY if encrypted
2124 0 : de_ser >> ACE_InputCDR::to_octet(type);
2125 0 : de_ser >> ACE_InputCDR::to_octet(flags);
2126 0 : de_ser.swap_bytes((flags & RTPS::FLAG_E) != ACE_CDR_BYTE_ORDER);
2127 0 : de_ser >> octetsToNext;
2128 0 : if (!de_ser.good_bit()) {
2129 0 : ACE_ERROR((LM_ERROR,
2130 : "(%P|%t) ERROR: CryptoBuiltInImpl::decode_submessage: "
2131 : "Failed to deserialize next submessage\n"));
2132 0 : return false;
2133 : }
2134 :
2135 0 : Message_Block_Ptr mb_footer(mb_in.duplicate());
2136 0 : mb_footer->rd_ptr(octetsToNext);
2137 : // SEC_POSTFIX
2138 0 : Serializer post_ser(mb_footer.get(), common_encoding);
2139 0 : post_ser >> ACE_InputCDR::to_octet(type);
2140 0 : post_ser >> ACE_InputCDR::to_octet(flags);
2141 0 : post_ser.swap_bytes((flags & RTPS::FLAG_E) != ACE_CDR_BYTE_ORDER);
2142 : ACE_CDR::UShort postfixOctetsToNext;
2143 0 : post_ser >> postfixOctetsToNext;
2144 0 : CryptoFooter cf;
2145 0 : de_ser.endianness(ENDIAN_BIG);
2146 0 : post_ser >> cf;
2147 0 : if (!post_ser.good_bit()) {
2148 0 : ACE_ERROR((LM_ERROR,
2149 : "(%P|%t) ERROR: CryptoBuiltInImpl::decode_submessage: "
2150 : "Failed to deserialize SEC_POST\n"));
2151 0 : return false;
2152 : }
2153 :
2154 0 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
2155 0 : const KeyTable_t::const_iterator keys_iter = keys_.find(sender_handle);
2156 0 : if (keys_iter == keys_.end()) {
2157 0 : return CommonUtilities::set_security_error(ex, -2, 3, "Crypto Key not found");
2158 : }
2159 :
2160 0 : const KeySeq& keyseq = keys_iter->second;
2161 0 : for (unsigned int i = 0; i < keyseq.length(); ++i) {
2162 0 : if (matches(keyseq[i], ch)) {
2163 0 : const KeyId_t sKey = std::make_pair(sender_handle, i);
2164 :
2165 0 : if (encrypts(keyseq[i])) {
2166 0 : de_ser.endianness(ENDIAN_BIG);
2167 : ACE_CDR::ULong n;
2168 0 : if (!(de_ser >> n)) {
2169 0 : ACE_ERROR((LM_ERROR,
2170 : "(%P|%t) ERROR: CryptoBuiltInImpl::decode_submessage: "
2171 : "Failed to deserialize content size(?)\n"));
2172 0 : return false;
2173 : }
2174 0 : return decrypt(keyseq[i], sessions_[sKey], mb_in.rd_ptr(), n, ch, cf,
2175 0 : plain_rtps_submessage, ex);
2176 :
2177 0 : } else if (authenticates(keyseq[i])) {
2178 0 : return verify(keyseq[i], sessions_[sKey], mb_in.rd_ptr() - RTPS::SMHDR_SZ,
2179 0 : RTPS::SMHDR_SZ + octetsToNext, ch, cf, plain_rtps_submessage, ex);
2180 :
2181 : } else {
2182 0 : return CommonUtilities::set_security_error(ex, -2, 2, "Key transform kind unrecognized");
2183 : }
2184 : }
2185 : }
2186 :
2187 0 : return CommonUtilities::set_security_error(ex, -2, 1, "Crypto Key not found");
2188 0 : }
2189 :
2190 3 : bool CryptoBuiltInImpl::decode_datawriter_submessage(
2191 : DDS::OctetSeq& plain_rtps_submessage,
2192 : const DDS::OctetSeq& encoded_rtps_submessage,
2193 : DatareaderCryptoHandle receiving_datareader_crypto,
2194 : DatawriterCryptoHandle sending_datawriter_crypto,
2195 : SecurityException& ex)
2196 : {
2197 : // Allowing Nil Handle for receiver since origin auth is not implemented:
2198 : // if (DDS::HANDLE_NIL == receiving_datareader_crypto) {
2199 : // return CommonUtilities::set_security_error(ex, -1, 0, "Invalid Datareader handle");
2200 : // }
2201 3 : if (DDS::HANDLE_NIL == sending_datawriter_crypto) {
2202 3 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid Datawriter handle");
2203 : }
2204 :
2205 0 : if (security_debug.encdec_debug) {
2206 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {encdec_debug} CryptoBuiltInImpl::decode_datawriter_submessage ")
2207 : ACE_TEXT("Sending DWCH is %u, Receiving DRCH is %u\n"),
2208 : sending_datawriter_crypto, receiving_datareader_crypto));
2209 : }
2210 :
2211 0 : return decode_submessage(plain_rtps_submessage, encoded_rtps_submessage,
2212 0 : sending_datawriter_crypto, ex);
2213 : }
2214 :
2215 1 : bool CryptoBuiltInImpl::decode_datareader_submessage(
2216 : DDS::OctetSeq& plain_rtps_submessage,
2217 : const DDS::OctetSeq& encoded_rtps_submessage,
2218 : DatawriterCryptoHandle receiving_datawriter_crypto,
2219 : DatareaderCryptoHandle sending_datareader_crypto,
2220 : SecurityException& ex)
2221 : {
2222 1 : if (DDS::HANDLE_NIL == sending_datareader_crypto) {
2223 1 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid Datareader handle");
2224 : }
2225 : // Allowing Nil Handle for receiver since origin auth is not implemented:
2226 : // if (DDS::HANDLE_NIL == receiving_datawriter_crypto) {
2227 : // return CommonUtilities::set_security_error(ex, -1, 0, "Invalid Datawriter handle");
2228 : // }
2229 :
2230 0 : if (security_debug.encdec_debug) {
2231 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {encdec_debug} CryptoBuiltInImpl::decode_datareader_submessage ")
2232 : ACE_TEXT("Sending DRCH is %u, Receiving DWCH is %u\n"),
2233 : sending_datareader_crypto, receiving_datawriter_crypto));
2234 : }
2235 :
2236 0 : return decode_submessage(plain_rtps_submessage, encoded_rtps_submessage,
2237 0 : sending_datareader_crypto, ex);
2238 : }
2239 :
2240 3 : bool CryptoBuiltInImpl::decode_serialized_payload(
2241 : DDS::OctetSeq& plain_buffer,
2242 : const DDS::OctetSeq& encoded_buffer,
2243 : const DDS::OctetSeq& /*inline_qos*/,
2244 : DatareaderCryptoHandle receiving_datareader_crypto,
2245 : DatawriterCryptoHandle sending_datawriter_crypto,
2246 : SecurityException& ex)
2247 : {
2248 : // Not currently requring a reader handle here, origin authentication
2249 : // for data payloads is not supported.
2250 : // if (DDS::HANDLE_NIL == receiving_datareader_crypto) {
2251 : // return CommonUtilities::set_security_error(ex, -1, 0, "Invalid Datareader handle");
2252 : // }
2253 3 : if (DDS::HANDLE_NIL == sending_datawriter_crypto) {
2254 2 : return CommonUtilities::set_security_error(ex, -1, 0, "Invalid Datawriter handle");
2255 : }
2256 :
2257 1 : if (security_debug.encdec_debug) {
2258 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {encdec_debug} CryptoBuiltInImpl::decode_serialized_payload ")
2259 : ACE_TEXT("Sending DWCH is %u, Receiving DRCH is %u\n"),
2260 : sending_datawriter_crypto, receiving_datareader_crypto));
2261 : }
2262 :
2263 1 : ACE_Guard<ACE_Thread_Mutex> guard(mutex_);
2264 1 : const KeyTable_t::const_iterator iter = keys_.find(sending_datawriter_crypto);
2265 1 : if (iter == keys_.end()) {
2266 0 : return CommonUtilities::set_security_error(ex, -1, 1, "No key for DataWriter crypto handle");
2267 : }
2268 1 : const EncryptOptions_t::const_iterator eo_iter = encrypt_options_.find(sending_datawriter_crypto);
2269 1 : if (eo_iter == encrypt_options_.end()) {
2270 0 : return CommonUtilities::set_security_error(ex, -1, 0, "Datawriter handle lacks encrypt options");
2271 : }
2272 1 : if (!eo_iter->second.payload_) {
2273 1 : plain_buffer = encoded_buffer;
2274 1 : if (security_debug.encdec_debug) {
2275 0 : ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%P|%t) {encdec_debug} CryptoBuiltInImpl::decode_serialized_payload ")
2276 : ACE_TEXT("Sending datawriter isn't encrypting as far as we know, returning input as plaintext\n"),
2277 : sending_datawriter_crypto, receiving_datareader_crypto));
2278 : }
2279 1 : return true;
2280 : }
2281 :
2282 : ACE_Message_Block mb_in(to_mb(encoded_buffer.get_buffer()),
2283 0 : encoded_buffer.length());
2284 0 : mb_in.wr_ptr(encoded_buffer.length());
2285 0 : Serializer de_ser(&mb_in, common_encoding);
2286 0 : CryptoHeader ch = CryptoHeader();
2287 0 : if (!(de_ser >> ch)) {
2288 0 : return CommonUtilities::set_security_error(ex, -3, 4, "Failed to deserialize CryptoHeader");
2289 : }
2290 :
2291 0 : const KeySeq& keyseq = iter->second;
2292 0 : for (unsigned int i = 0; i < keyseq.length(); ++i) {
2293 0 : if (matches(keyseq[i], ch)) {
2294 0 : const KeyId_t sKey = std::make_pair(sending_datawriter_crypto, i);
2295 0 : if (encrypts(keyseq[i])) {
2296 : ACE_CDR::ULong n;
2297 0 : if (!(de_ser >> n)) {
2298 0 : return CommonUtilities::set_security_error(ex, -3, 5, "Failed to deserialize CryptoContent length");
2299 : }
2300 0 : const char* const ciphertext = mb_in.rd_ptr();
2301 0 : if (!de_ser.skip(n)) {
2302 0 : return CommonUtilities::set_security_error(ex, -3, 7, "Failed to locate CryptoFooter");
2303 : }
2304 0 : CryptoFooter cf;
2305 0 : if (!(de_ser >> cf)) {
2306 0 : return CommonUtilities::set_security_error(ex, -3, 6, "Failed to deserialize CryptoFooter");
2307 : }
2308 0 : return decrypt(keyseq[i], sessions_[sKey], ciphertext, n, ch, cf, plain_buffer, ex);
2309 :
2310 0 : } else if (authenticates(keyseq[i])) {
2311 0 : return CommonUtilities::set_security_error(ex, -3, 3, "Auth-only payload "
2312 : "transformation not supported "
2313 0 : "(DDSSEC12-59)");
2314 :
2315 : } else {
2316 0 : return CommonUtilities::set_security_error(ex, -3, 2, "Key transform kind unrecognized");
2317 : }
2318 : }
2319 : }
2320 :
2321 0 : return CommonUtilities::set_security_error(ex, -3, 1, "Crypto Key not found");
2322 1 : }
2323 :
2324 : }
2325 : }
2326 :
2327 : OPENDDS_END_VERSIONED_NAMESPACE_DECL
|