1 /*
2 * h2356.cxx
3 *
4 * H.235.6 Encryption class.
5 *
6 * h323plus library
7 *
8 * Copyright (c) 2011 Spranto Australia Pty Ltd.
9 *
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.1 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
14 *
15 * Alternatively, the contents of this file may be used under the terms
16 * of the General Public License (the "GNU License"), in which case the
17 * provisions of GNU License are applicable instead of those
18 * above. If you wish to allow use of your version of this file only
19 * under the terms of the GNU License and not to allow others to use
20 * your version of this file under the MPL, indicate your decision by
21 * deleting the provisions above and replace them with the notice and
22 * other provisions required by the GNU License. If you do not delete
23 * the provisions above, a recipient may use your version of this file
24 * under either the MPL or the GNU License."
25 *
26 * Software distributed under the License is distributed on an "AS IS"
27 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
28 * the License for the specific language governing rights and limitations
29 * under the License.
30 *
31 *
32 * The Initial Developer of the Original Code is ISVO (Asia) Pte. Ltd.
33 *
34 *
35 * Contributor(s): ______________________________________.
36 *
37 * $Id$
38 *
39 *
40 */
41
42
43 #include <ptlib.h>
44 #include "openh323buildopts.h"
45
46 #ifdef H323_H235
47
48 #include <h235auth.h>
49 #include "h235/h2356.h"
50 #include "h235/h235support.h"
51 #include "h323con.h"
52 #include <algorithm>
53
54
55 ////////////////////////////////////////////////////////////////////////////////////
56 // Helper functions
57
58 template <class PAIR>
59 class deletepair { // PAIR::second_type is a pointer type
60 public:
operator ()(const PAIR & p)61 void operator()(const PAIR & p) { if (p.second) delete p.second; }
62 };
63
64 template <class M>
DeleteObjectsInMap(M & m)65 inline void DeleteObjectsInMap(M & m)
66 {
67 typedef typename M::value_type PAIR;
68 std::for_each(m.begin(), m.end(), deletepair<PAIR>());
69 m.clear(); // delete pointers to deleted objects
70 }
71
IsSupportedOID(const PString & oid,unsigned cipherlength)72 PBoolean IsSupportedOID(const PString & oid, unsigned cipherlength)
73 {
74 // Check key length
75 for (PINDEX i = 0; i < PARRAYSIZE(H235_DHCustom); ++i) {
76 if (H235_DHCustom[i].parameterOID == oid) {
77 if (H235_DHCustom[i].sz <= (cipherlength * 8))
78 return true;
79 else
80 return false;
81 }
82 }
83 return false;
84 }
85
LoadH235_DHMap(H235_DHMap & dhmap,H235_DHMap & dhcache,H235Authenticators::DH_DataList & customData,const PString & filePath=PString (),PINDEX cipherlength=P_MAX_INDEX,PINDEX maxTokenLength=1024)86 void LoadH235_DHMap(H235_DHMap & dhmap, H235_DHMap & dhcache, H235Authenticators::DH_DataList & customData, const PString & filePath = PString(), PINDEX cipherlength = P_MAX_INDEX, PINDEX maxTokenLength = 1024)
87 {
88 if (!dhcache.empty()) {
89 H235_DHMap::iterator i = dhcache.begin();
90 while (i != dhcache.end()) {
91 if (i->second)
92 dhmap.insert(pair<PString, H235_DiffieHellman*>(i->first, (H235_DiffieHellman*)i->second->Clone()));
93 else
94 dhmap.insert(pair<PString, H235_DiffieHellman*>(i->first, (H235_DiffieHellman*)NULL));
95 ++i;
96 }
97 return;
98 }
99
100 // Load from memory vendor supplied keys
101 if (!customData.empty()) {
102 H235Authenticators::DH_DataList::iterator r;
103 for (r = customData.begin(); r != customData.end(); ++r) {
104 if (IsSupportedOID(r->m_OID, cipherlength)) {
105 dhmap.insert(pair<PString, H235_DiffieHellman*>(r->m_OID,
106 new H235_DiffieHellman(r->m_pData.GetPointer(), r->m_pData.GetSize(),
107 r->m_gData, r->m_gData.GetSize(), true)));
108 PTRACE(6, "H2356\tMemory KeyPair " << r->m_OID << " loaded.");
109 } else {
110 PTRACE(6, "H2356\tMemory KeyPair " << r->m_OID << " ignored.");
111 }
112 }
113 }
114
115 // Load from vendor supplied filePath
116 PStringArray FilePaths;
117 if (!filePath.IsEmpty()) {
118 PStringArray temp = filePath.Tokenise(';');
119 for (PINDEX k = 0; k < temp.GetSize(); ++k) {
120 PFilePath dhFile = PString(temp[k]);
121 if (PFile::Exists(dhFile)) {
122 FilePaths.AppendString(dhFile);
123 }
124 }
125 }
126
127 // Load default DH keypairs
128 for (PINDEX k = 0; k < FilePaths.GetSize(); ++k) {
129 PConfig cfg(FilePaths[k], PString());
130 PStringArray oidList(cfg.GetSections());
131 for (PINDEX j = 0; j < oidList.GetSize(); ++j) {
132 if (IsSupportedOID(oidList[j], cipherlength)) {
133 H235_DiffieHellman * dh = new H235_DiffieHellman(cfg, oidList[j]);
134 if (dh->LoadedFromFile()) {
135 dhmap.insert(pair<PString, H235_DiffieHellman*>(oidList[j], dh));
136 PTRACE(6, "H2356\tFile KeyPair " << oidList[j] << " loaded.");
137 } else {
138 PTRACE(1, "H2356\tError: Loading DH key file failed");
139 delete dh;
140 }
141 } else {
142 PTRACE(6, "H2356\tFile KeyPair " << oidList[j] << " ignored.");
143 }
144 }
145 }
146
147 // if not loaded from File then create.
148 for (PINDEX i = 0; i < PARRAYSIZE(H235_DHParameters); ++i) {
149 if (dhmap.find(H235_DHParameters[i].parameterOID) == dhmap.end()) {
150 if (H235_DHParameters[i].sz > 0 && H235_DHParameters[i].cipher <= (unsigned)cipherlength
151 && (H235_DHParameters[i].dh_p == NULL || (H235_DHParameters[i].sz * 8) <= (unsigned)maxTokenLength)) {
152 dhmap.insert(pair<PString, H235_DiffieHellman*>(H235_DHParameters[i].parameterOID,
153 new H235_DiffieHellman(H235_DHParameters[i].dh_p, H235_DHParameters[i].sz,
154 H235_DHParameters[i].dh_g, H235_DHParameters[i].sz,
155 H235_DHParameters[i].send)) );
156 PTRACE(6, "H2356\tStd KeyPair " << H235_DHParameters[i].parameterOID << " loaded.");
157 } else if (H235_DHParameters[i].cipher == 0) {
158 dhmap.insert(pair<PString, H235_DiffieHellman*>(H235_DHParameters[i].parameterOID, (H235_DiffieHellman*)NULL));
159 PTRACE(6, "H2356\tStd KeyPair " << H235_DHParameters[i].parameterOID << " loaded.");
160 } else
161 PTRACE(6, "H2356\tStd KeyPair " << H235_DHParameters[i].parameterOID << " ignored.");
162 continue; // Ignore ciphers greater that cipherlength
163 }
164 }
165
166 }
167
168 /////////////////////////////////////////////////////////////////////////////////////
169
170 #if PTLIB_VER >= 2110
171 #ifdef H323_SSL
172 H235SECURITY(Std6);
173 #endif
174 #else
175 static PFactory<H235Authenticator>::Worker<H2356_Authenticator> factoryH2356_Authenticator("H2356_Authenticator");
176 #endif
177
178 H235_DHMap H2356_Authenticator::m_dhCachedMap;
179
H2356_Authenticator()180 H2356_Authenticator::H2356_Authenticator()
181 : m_tokenState(e_clearNone)
182 {
183 usage = MediaEncryption;
184
185 m_enabled = (H235Authenticators::GetEncryptionPolicy() > 0);
186 m_active = m_enabled;
187
188 m_algOIDs.SetSize(0);
189 if (m_enabled) {
190 LoadH235_DHMap(m_dhLocalMap, m_dhCachedMap, H235Authenticators::GetDHDataList(), H235Authenticators::GetDHParameterFile(), H235Authenticators::GetMaxCipherLength(), H235Authenticators::GetMaxTokenLength());
191 InitialiseSecurity(); // make sure m_algOIDs gets filled
192 }
193 }
194
~H2356_Authenticator()195 H2356_Authenticator::~H2356_Authenticator()
196 {
197 DeleteObjectsInMap(m_dhLocalMap);
198 }
199
GetAuthenticatorNames()200 PStringArray H2356_Authenticator::GetAuthenticatorNames()
201 {
202 return PStringArray("Std6");
203 }
204
205 #if PTLIB_VER >= 2110
GetAuthenticationCapabilities(H235Authenticator::Capabilities * ids)206 PBoolean H2356_Authenticator::GetAuthenticationCapabilities(H235Authenticator::Capabilities * ids)
207 {
208 for (PINDEX i = 0; i < PARRAYSIZE(H235_Encryptions); ++i) {
209 H235Authenticator::Capability cap;
210 cap.m_identifier = H235_Encryptions[i].algorithmOID;
211 cap.m_cipher = H235_Encryptions[i].sslDesc;
212 cap.m_description= H235_Encryptions[i].desc;
213 ids->capabilityList.push_back(cap);
214 }
215 return true;
216 }
217 #endif
218
InitialiseCache(int cipherlength,unsigned maxTokenLength)219 void H2356_Authenticator::InitialiseCache(int cipherlength, unsigned maxTokenLength)
220 {
221 LoadH235_DHMap(m_dhCachedMap, m_dhCachedMap, H235Authenticators::GetDHDataList(), H235Authenticators::GetDHParameterFile(), cipherlength, maxTokenLength);
222 }
223
RemoveCache()224 void H2356_Authenticator::RemoveCache()
225 {
226 DeleteObjectsInMap(m_dhCachedMap);
227 m_dhCachedMap.clear();
228 }
229
IsMatch(const PString & identifier) const230 PBoolean H2356_Authenticator::IsMatch(const PString & identifier) const
231 {
232 PStringArray ids;
233 for (PINDEX i = 0; i < PARRAYSIZE(H235_DHParameters); ++i) {
234 if (PString(H235_DHParameters[i].parameterOID) == identifier)
235 return true;
236 }
237 return false;
238 }
239
240
Clone() const241 PObject * H2356_Authenticator::Clone() const
242 {
243 H2356_Authenticator * auth = new H2356_Authenticator(*this);
244
245 // We do NOT copy these fields in Clone()
246 auth->lastRandomSequenceNumber = 0;
247 auth->lastTimestamp = 0;
248
249 return auth;
250 }
251
GetName() const252 const char * H2356_Authenticator::GetName() const
253 {
254 return "H.235.6";
255 }
256
PrepareTokens(PASN_Array & clearTokens,PASN_Array &,PINDEX max_keyLength)257 PBoolean H2356_Authenticator::PrepareTokens(PASN_Array & clearTokens,
258 PASN_Array & /*cryptoTokens*/,
259 PINDEX max_keyLength)
260 {
261 if (!IsActive() || (m_tokenState == e_clearDisable) || (max_keyLength==0))
262 return FALSE;
263
264 H225_ArrayOf_ClearToken & tokens = (H225_ArrayOf_ClearToken &)clearTokens;
265
266 H235_DHMap::iterator i = m_dhLocalMap.begin();
267 while (i != m_dhLocalMap.end()) {
268 H235_DiffieHellman * dh = i->second;
269 if (dh && (dh->GetKeyLength() > (max_keyLength/8))) {
270 ++i; continue;
271 }
272
273 // Build the token
274 int sz = tokens.GetSize();
275 tokens.SetSize(sz+1);
276 H235_ClearToken & clearToken = tokens[sz];
277 clearToken.m_tokenOID = i->first;
278 if (dh && dh->GenerateHalfKey()) {
279 if (dh->GetKeySize() <= 256) { // Key Size 2048 or smaller
280 clearToken.IncludeOptionalField(H235_ClearToken::e_dhkey);
281 H235_DHset & dhkey = clearToken.m_dhkey;
282 dh->Encode_HalfKey(dhkey.m_halfkey);
283 dh->Encode_P(dhkey.m_modSize);
284 dh->Encode_G(dhkey.m_generator);
285 } else { // Key Size > 2048
286 clearToken.IncludeOptionalField(H235_ClearToken::e_dhkeyext);
287 H235_DHsetExt & dhkey = clearToken.m_dhkeyext;
288 dh->Encode_HalfKey(dhkey.m_halfkey);
289 if (dh->Encode_P(dhkey.m_modSize))
290 dhkey.IncludeOptionalField(H235_DHsetExt::e_modSize);
291 if (dh->Encode_G(dhkey.m_generator))
292 dhkey.IncludeOptionalField(H235_DHsetExt::e_generator);
293 }
294 }
295 ++i;
296 }
297
298 if (m_tokenState == e_clearNone) {
299 m_tokenState = e_clearSent;
300 return true;
301 }
302
303 if (m_tokenState == e_clearReceived) {
304 InitialiseSecurity();
305 m_tokenState = e_clearComplete;
306 }
307
308 return true;
309 }
310
ValidateTokens(const PASN_Array & clearTokens,const PASN_Array &,const PBYTEArray &)311 H235Authenticator::ValidationResult H2356_Authenticator::ValidateTokens(const PASN_Array & clearTokens,
312 const PASN_Array & /*cryptoTokens*/, const PBYTEArray & /*rawPDU*/)
313 {
314 if (!IsActive() || (m_tokenState == e_clearDisable))
315 return e_Disabled;
316
317 const H225_ArrayOf_ClearToken & tokens = (const H225_ArrayOf_ClearToken &)clearTokens;
318 if (tokens.GetSize() == 0) {
319 DeleteObjectsInMap(m_dhLocalMap);
320 m_tokenState = e_clearDisable;
321 return e_Disabled;
322 }
323
324 PString selectOID;
325 H235_DHMap::iterator it;
326 for (PINDEX i = 0; i < tokens.GetSize(); ++i) {
327 for (it = m_dhLocalMap.begin(); it != m_dhLocalMap.end(); ++it) {
328 const H235_ClearToken & token = tokens[i];
329 PString tokenOID = token.m_tokenOID.AsString();
330 if (it->first == tokenOID) {
331 if (it->second != NULL) {
332 if (token.HasOptionalField(H235_ClearToken::e_dhkey)) { // For keysize up to and including 2048
333 const H235_DHset & dh = token.m_dhkey;
334 it->second->SetRemoteHalfKey(dh.m_halfkey);
335 if (!m_tokenState && dh.m_modSize.GetSize() > 0) // replace p and g if included and received first
336 it->second->SetDHReceived(dh.m_modSize,dh.m_generator);
337
338 } else if (token.HasOptionalField(H235_ClearToken::e_dhkeyext)) { // For keysize greater then 2048
339 const H235_DHsetExt & dh = token.m_dhkeyext;
340 it->second->SetRemoteHalfKey(dh.m_halfkey);
341 if (!m_tokenState && dh.HasOptionalField(H235_DHsetExt::e_modSize) && // replace p and g if included if received first
342 dh.HasOptionalField(H235_DHsetExt::e_generator))
343 it->second->SetDHReceived(dh.m_modSize,dh.m_generator);
344
345 } else {
346 PTRACE(2, "H2356\tERROR DH Parameters missing " << it->first << " skipping.");
347 continue;
348 }
349 selectOID = it->first;
350 PTRACE(4, "H2356\tSetting Encryption Algorithm for call " << selectOID);
351 }
352 }
353 if (!selectOID) break;
354 }
355 if (!selectOID) break;
356 }
357
358 if (!selectOID) {
359 // Remove unmatched authenticators
360 it = m_dhLocalMap.begin();
361 while (it != m_dhLocalMap.end()) {
362 if (it->second && selectOID != it->first) {
363 PTRACE(4, "H2356\tRemoving unmatched Encryption Algorithm " << it->first);
364 delete it->second;
365 m_dhLocalMap.erase(it++);
366 } else
367 ++it;
368 }
369 } else
370 DeleteObjectsInMap(m_dhLocalMap);
371
372 if (m_dhLocalMap.size() == 0) {
373 PTRACE(4, "H2356\tNo matching Encryption Algorithms. Encryption Disabled!");
374 m_tokenState = e_clearDisable;
375 return e_Absent;
376 }
377
378 if (m_tokenState == e_clearNone) {
379 m_tokenState = e_clearReceived;
380 return e_OK;
381 }
382
383 if (m_tokenState == e_clearSent) {
384 m_tokenState = e_clearComplete;
385 InitialiseSecurity();
386 }
387
388 return e_OK;
389 }
390
IsSecuredSignalPDU(unsigned signalPDU,PBoolean) const391 PBoolean H2356_Authenticator::IsSecuredSignalPDU(unsigned signalPDU, PBoolean /*received*/) const
392 {
393 switch (signalPDU) {
394 case H225_H323_UU_PDU_h323_message_body::e_setup:
395 case H225_H323_UU_PDU_h323_message_body::e_connect:
396 return enabled;
397 default :
398 return false;
399 }
400 }
401
IsSecuredPDU(unsigned,PBoolean) const402 PBoolean H2356_Authenticator::IsSecuredPDU(unsigned /*rasPDU*/, PBoolean /*received*/) const
403 {
404 return false;
405 }
406
IsCapability(const H235_AuthenticationMechanism &,const PASN_ObjectId &)407 PBoolean H2356_Authenticator::IsCapability(const H235_AuthenticationMechanism & /*mechansim*/, const PASN_ObjectId & /*algorithmOID*/)
408 {
409 return false;
410 }
411
SetCapability(H225_ArrayOf_AuthenticationMechanism &,H225_ArrayOf_PASN_ObjectId &)412 PBoolean H2356_Authenticator::SetCapability(H225_ArrayOf_AuthenticationMechanism & /*mechansim*/, H225_ArrayOf_PASN_ObjectId & /*algorithmOIDs*/)
413 {
414 return false;
415 }
416
IsActive() const417 PBoolean H2356_Authenticator::IsActive() const
418 {
419 return m_active;
420 }
421
Disable()422 void H2356_Authenticator::Disable()
423 {
424 m_enabled = false;
425 m_active = false;
426 }
427
InitialiseSecurity()428 void H2356_Authenticator::InitialiseSecurity()
429 {
430 PString dhOID;
431 int lastKeyLength = 0;
432 H235_DHMap::iterator i = m_dhLocalMap.begin();
433 while (i != m_dhLocalMap.end()) {
434 if (i->second && i->second->GetKeyLength() > lastKeyLength) {
435 dhOID = i->first;
436 lastKeyLength = i->second->GetKeyLength();
437 }
438 ++i;
439 }
440
441 if (dhOID.IsEmpty())
442 return;
443
444 m_algOIDs.SetSize(0);
445 for (PINDEX i=0; i<PARRAYSIZE(H235_Algorithms); ++i) {
446 if (PString(H235_Algorithms[i].DHparameters) == dhOID)
447 m_algOIDs.AppendString(H235_Algorithms[i].algorithm);
448 }
449
450 H235_DHMap::iterator l = m_dhLocalMap.find(dhOID);
451
452 if (connection && l != m_dhLocalMap.end()) {
453 H235Capabilities * localCaps = (H235Capabilities *)connection->GetLocalCapabilitiesRef();
454 localCaps->SetDHKeyPair(m_algOIDs,l->second,connection->IsH245Master());
455 }
456 }
457
GetMediaSessionInfo(PString & algorithmOID,PBYTEArray & sessionKey)458 PBoolean H2356_Authenticator::GetMediaSessionInfo(PString & algorithmOID, PBYTEArray & sessionKey)
459 {
460
461 if (m_algOIDs.GetSize() == 0) {
462 PTRACE(1, "H235\tNo algorithms available");
463 return false;
464 }
465
466 PString DhOID = GetDhOIDFromAlg(m_algOIDs[0]);
467 H235_DHMap::const_iterator l = m_dhLocalMap.find(DhOID);
468 if (l != m_dhLocalMap.end()) {
469 algorithmOID = m_algOIDs[0];
470 return l->second->ComputeSessionKey(sessionKey);
471 }
472 return false;
473 }
474
GetAlgorithms(PStringList & algorithms) const475 PBoolean H2356_Authenticator::GetAlgorithms(PStringList & algorithms) const
476 {
477 algorithms = m_algOIDs;
478 return (m_algOIDs.GetSize() > 0);
479 }
480
GetAlgorithmDetails(const PString & algorithm,PString & sslName,PString & description)481 PBoolean H2356_Authenticator::GetAlgorithmDetails(const PString & algorithm, PString & sslName, PString & description)
482 {
483 for (PINDEX i=0; i<PARRAYSIZE(H235_Encryptions); ++i) {
484 if (PString(H235_Encryptions[i].algorithmOID) == algorithm) {
485 sslName = H235_Encryptions[i].sslDesc;
486 description = H235_Encryptions[i].desc;
487 return true;
488 }
489 }
490 return false;
491 }
492
GetAlgFromOID(const PString & oid)493 PString H2356_Authenticator::GetAlgFromOID(const PString & oid)
494 {
495 if (oid.IsEmpty())
496 return PString();
497
498 for (PINDEX i = 0; i < PARRAYSIZE(H235_Encryptions); ++i) {
499 if (PString(H235_Encryptions[i].algorithmOID) == oid)
500 return H235_Encryptions[i].sslDesc;
501 }
502 return PString();
503 }
504
GetOIDFromAlg(const PString & sslName)505 PString H2356_Authenticator::GetOIDFromAlg(const PString & sslName)
506 {
507 if (sslName.IsEmpty())
508 return PString();
509
510 for (PINDEX i = 0; i < PARRAYSIZE(H235_Encryptions); ++i) {
511 if (H235_Encryptions[i].sslDesc == sslName)
512 return PString(H235_Encryptions[i].algorithmOID);
513 }
514 return PString();
515 }
516
GetDhOIDFromAlg(const PString & alg)517 PString H2356_Authenticator::GetDhOIDFromAlg(const PString & alg)
518 {
519 if (alg.IsEmpty())
520 return PString();
521
522 for (PINDEX i = 0; i < PARRAYSIZE(H235_Algorithms); ++i) {
523 if (PString(H235_Algorithms[i].algorithm) == alg)
524 return H235_Algorithms[i].DHparameters;
525 }
526 return PString();
527 }
528
ExportParameters(const PFilePath & path)529 void H2356_Authenticator::ExportParameters(const PFilePath & path)
530 {
531 H235_DHMap::iterator i = m_dhLocalMap.begin();
532 while (i != m_dhLocalMap.end()) {
533 if (i->second && i->second->GetKeyLength() > 0) {
534 i->second->Save(path,i->first);
535 }
536 ++i;
537 }
538 }
539
540 #else
541
542 #ifdef _MSC_VER
543 #pragma message("H.235 Media support DISABLED (missing OpenSSL)")
544 #endif
545
546 #endif // H323_H235
547
548