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