1 /*
2  *
3  *  Copyright (C) 2018-2019, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *
14  *  Module: dcmtls
15  *
16  *  Author: Marco Eichelberg
17  *
18  *  Purpose:
19  *    classes: DcmTLSCiphersuiteHandler
20  *
21  */
22 
23 #ifndef TLSCIPHR_H
24 #define TLSCIPHR_H
25 
26 #include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
27 
28 #ifdef WITH_OPENSSL
29 
30 #include "dcmtk/ofstd/ofstring.h"     /* for class OFString */
31 #include "dcmtk/ofstd/ofstream.h"     /* for class ostream */
32 #include "dcmtk/ofstd/ofvector.h"     /* for class OFVector */
33 #include "dcmtk/dcmtls/tlsdefin.h"    /* for DCMTK_DCMTLS_EXPORT */
34 #include "dcmtk/dcmnet/dcmlayer.h"    /* for DcmTransportLayerStatus */
35 
36 // include this file in doxygen documentation
37 
38 /** @file tlsciphr.h
39  *  @brief type definitions and classes for TLS ciphersuites
40  */
41 
42 /** This enum describes DICOM TLS Security Profiles, which are the basis
43  *  for the selection of supported ciphersuites when using DICOM over TLS.
44  *  @remark This enum is only available if DCMTK is compiled with
45  *  OpenSSL support enabled.
46  */
47 enum DcmTLSSecurityProfile
48 {
49   /// No security profile, start with an empty list of ciphersuites
50   TSP_Profile_None,
51 
52   /** DICOM Basic TLS Secure Transport Connection Profile (retired),
53     * using the ciphersuite TLS_RSA_WITH_3DES_EDE_CBC_SHA and TLS 1.0 or newer.
54     */
55   TSP_Profile_Basic,
56 
57   /** DICOM AES TLS Secure Transport Connection Profile (retired),
58     * using the ciphersuites TLS_RSA_WITH_AES_128_CBC_SHA and TLS_RSA_WITH_3DES_EDE_CBC_SHA,
59     * and TLS 1.0 or newer.
60     */
61   TSP_Profile_AES,
62 
63   /** DICOM BCP 195 TLS Profile, based on RFC 7525.
64    *  This profile tries to negotiate TLS 1.2 or newer, but may fall back to previous
65    *  protocol versions up to TLS 1.0. It supports four TLS 1.2 specific ciphersuites
66    *  (TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
67    *  TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
68    *  but also the ciphersuites of the AES and Basic Profile, thus providing
69    *  backward compatibility with older implementations while offering much
70    *  better security when used with implementations also supporting one of the
71    *  BCP 195 profiles.
72    */
73   TSP_Profile_BCP195,
74 
75   /** DICOM Non-downgrading BCP 195 TLS Profile, based on RFC 7525.
76    *  This profile only negotiates TLS 1.2 or newer, and will not fall back to
77    *  previous TLS versions. It supports four ciphersuites:
78    *  TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
79    *  TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384.
80    *  It does not provide backwards compatibility with the older Basic and AES profiles,
81    *  and thus guarantees the higher security level of BCP 195.
82    */
83   TSP_Profile_BCP195_ND,
84 
85   /** DICOM Extended BCP 195 TLS Profile, based on RFC 7525.
86    *  This profile only negotiates TLS 1.2, and will not fall back to
87    *  previous TLS versions. It does NOT support TLS 1.3.
88    *  It supports the same set of ciphersuites as TSP_Profile_BCP195_ND,
89    *  plus TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 and TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.
90    *  The other ciphersuites suggested by the profile (see DICOM Part 15, section B.11)
91    *  are not supported in OpenSSL 1.0.1 to 1.1.0.
92    *  This profile requires DHE keys of at least 2048 bits and ECDHE keys of at least 256 bits.
93    *  It does not provide backwards compatibility with the older Basic and AES profiles,
94    *  and thus guarantees the higher security level of BCP 195.
95    */
96   TSP_Profile_BCP195_Extended,
97 
98   /** IHE ATNA Profile for Unencrypted In-house Communication (retired).
99    * This profile uses the ciphersuite SSL_RSA_WITH_NULL_SHA and TLS 1.0 or newer.
100    * This ciphersuite offers peer authentication and integrity of communication,
101    * but no encryption (i.e. no confidentiality), and is thus only suitable for use
102    * over a secure infrastructure. This was used in older versions of the
103    * IHE Audit Trail and Node Authentication (ATNA) integration profile,
104    * but has been retired.
105    */
106   TSP_Profile_IHE_ATNA_Unencrypted
107 
108 };
109 
110 
111 /** This enum describes the minimum SSL/TLS protocol version required
112  *  for a certain ciphersuite.
113  *  @remark this enum is only available if DCMTK is compiled with
114  *  OpenSSL support enabled.
115  */
116 enum DcmTLSCipherProtocolVersion
117 {
118   /// Secure Socket Layer (SSL) version 3 or newer
119   TPV_SSLv3,
120   /// Transport Layer Security (TLS) version 1.2 or newer
121   TPV_TLSv12
122 };
123 
124 
125 /** This enum describes the key exchange protocol used in a certain
126  *  TLS ciphersuite.
127  *  @remark this enum is only available if DCMTK is compiled with
128  *  OpenSSL support enabled.
129  */
130 enum DcmTLSCipherKeyExchange
131 {
132   /// Static RSA key exchange
133   TKE_RSA,
134 
135   /// Elliptic Curve Diffie–Hellman
136   TKE_ECDH,
137 
138   /// Fixed ECDH with ECDSA-signed certificates
139   TKE_ECDH_ECDSA,
140 
141   /// Fixed ECDH with RSA signatures
142   TKE_ECDH_RSA,
143 
144   /// Diffie–Hellman key exchange
145   TKE_DH
146 
147 };
148 
149 
150 /** This enum describes the authentication mechanism
151  *  used in a certain TLS ciphersuite.
152  *  @remark this enum is only available if DCMTK is compiled with
153  *  OpenSSL support enabled.
154  */
155 enum DcmTLSCipherAuthentication
156 {
157   /// Digital Signature Standard
158   TCA_DSS,
159 
160   /// Elliptic Curve Diffie–Hellman
161   TCA_ECDH,
162 
163   /// Elliptic Curve Digital Signature Algorithm
164   TCA_ECDSA,
165 
166   /// RSA
167   TCA_RSA
168 };
169 
170 
171 /** This enum describes the symmetric encryption algorithm
172  *  (bulk cipher) used in a certain TLS ciphersuite.
173  *  @remark this enum is only available if DCMTK is compiled with
174  *  OpenSSL support enabled.
175  */
176 enum DcmTLSCipherEncryption
177 {
178   /// No encryption
179   TCE_None,
180 
181   /// Triple DES in Cipher Block Chaining (CBC) mode
182   TCE_3DES,
183 
184   /// Advanced Encryption Standard (AES) in Cipher Block Chaining (CBC) mode
185   TCE_AES,
186 
187   /// Advanced Encryption Standard (AES) in Galois/Counter Mode (GCM)
188   TCE_AESGCM,
189 
190   /// Camellia (RFC 3713) in Cipher Block Chaining (CBC) mode
191   TCE_Camellia,
192 
193   /// ChaCha20-Poly1305 in Galois/Counter Mode (GCM)
194   TCE_ChaCha20
195 };
196 
197 
198 /** This enum describes the message authentication code (MAC)
199  *  used in a certain TLS ciphersuite.
200  *  @remark this enum is only available if DCMTK is compiled with
201  *  OpenSSL support enabled.
202  */
203 enum DcmTLSCipherMAC
204 {
205   /// Secure Hash Algorithm SHA-1
206   TCM_SHA1,
207 
208   /// Secure Hash Algorithm SHA-2 in 256 bit mode
209   TCM_SHA256,
210 
211   /// Secure Hash Algorithm SHA-2 in 384 bit mode
212   TCM_SHA384,
213 
214   /// Authenticated Encryption with Associated Data (AEAD)
215   TCM_AEAD
216 
217 };
218 
219 
220 /** This helper class manages the list of TLS ciphersuites supported by DCMTK,
221  *  translates DcmTLSSecurityProfile enums into the corresponding sets of
222  *  ciphersuites, and permits translation between the official TLS ciphersuite
223  *  names (as used in DCMTK) and the corresponding internal OpenSSL name.
224  *  @remark This class is only available if DCMTK is compiled with
225  *  OpenSSL support enabled.
226  */
227 class DCMTK_DCMTLS_EXPORT DcmTLSCiphersuiteHandler
228 {
229 public:
230 
231   /** constructor. The constructor assumes that the OpenSSL library
232    *  has already been initialized. This should be ensured prior to creating
233    *  any DcmTLSCiphersuiteHandler instance by calling
234    *  DcmTLSTransportLayer::initializeOpenSSL(). This only needs to be done once.
235    */
236   DcmTLSCiphersuiteHandler();
237 
238   /// destructor
239   virtual ~DcmTLSCiphersuiteHandler();
240 
241   /** replace the current list of ciphersuites by the list of ciphersuites
242    *  for the given profile.
243    *  @param profile TLS Security Profile
244    *  @return TCS_ok if successful, an error code otherwise
245    */
246   DcmTransportLayerStatus setTLSProfile(DcmTLSSecurityProfile profile);
247 
248   /** return the currently selected TLS profile
249    *  @return currently selected TLS profile
250    */
getTLSProfile()251   DcmTLSSecurityProfile getTLSProfile() const
252   {
253       return currentProfile;
254   }
255 
256   /** clear the current list of ciphersuites. Equivalent to
257    *  calling setTLSProfile(TSP_Profile_None).
258    */
259   void clearTLSProfile();
260 
261   /** adds a ciphersuite to the list of ciphersuites for TLS negotiation.
262    *  It is the responsibility of the user to ensure that the added ciphersuite
263    *  does not break the rules of the selected profile. Use with care!
264    *  @param suite TLS ciphersuite name, in the official TLS name form.
265    *  @return TCS_ok if successful, an error code otherwise
266    */
267   DcmTransportLayerStatus addCipherSuite(const char *suite);
268 
269   /** returns a string in OpenSSL syntax that contains the currently defined
270    *  list of TLS ciphersuites.
271    *  @param cslist The list of ciphersuites in OpenSSL syntax is written to this string.
272    *  @param isServer true if the list of cipher suites is intended for
273    *    a TLS server. In this case, the list of ciphersuites will be reordered
274    *    from strongest to weakest, as recommended by BCP 195.
275    */
276   void getListOfCipherSuitesForOpenSSL(OFString& cslist, OFBool isServer) const;
277 
278   /** returns the set of flags that need to be activated in OpenSSL
279    *  depending on the selected TLS profile.
280    *  @return flags for openssl
281    */
282   long getTLSOptions() const;
283 
284   /** checks if the 3DES ciphersuite TLS_RSA_WITH_3DES_EDE_CBC_SHA
285    *  is supported by the underlying OpenSSL library (which newer versions
286    *  only do if compiled with "weak ciphers" enabled).
287    *  @return true if we support 3DES, false otherwise
288    */
289   OFBool cipher3DESsupported() const;
290 
291   /** checks if the unencrypted ciphersuite TLS_RSA_WITH_NULL_SHA
292    *  is supported by the underlying OpenSSL library (which newer versions
293    *  only do if compiled with "weak ciphers" enabled).
294    *  @return true if we support unencrypted TLS, false otherwise
295    */
296   OFBool cipherNULLsupported() const;
297 
298   /** checks if TLS 1.3 is enabled (permitted) for the currently selected
299    *  TLS security profile. Note that this does not imply that the underlying
300    *  OpenSSL library version actually supports TLS 1.3. That is checked elsewhere.
301    *  @return true if we support TLS 1.3, false otherwise
302    */
303   OFBool isTLS13Enabled() const;
304 
305   /** print a list of supported ciphersuites to the given output stream
306    *  @param os output stream
307    */
308   void printSupportedCiphersuites(STD_NAMESPACE ostream& os) const;
309 
310   /** returns the number of known ciphersuites.
311    *  @return number of known ciphersuites
312    */
313   static size_t getNumberOfCipherSuites();
314 
315   /** looks up the index of the given ciphersuite by name
316    *  @param tlsCipherSuiteName ciphersuite name in RFC 2246 form
317    *  @returns index into list of ciphersuites, DcmTLSCiphersuiteHandler::unknownCipherSuiteIndex if ciphersuite unknown
318    */
319   static size_t lookupCiphersuite(const char *tlsCipherSuiteName);
320 
321   /** looks up the index of the given ciphersuite by OpenSSL name
322    *  @param tlsCipherSuiteName ciphersuite name in the form used by OpenSSL
323    *  @returns index into list of ciphersuites, DcmTLSCiphersuiteHandler::unknownCipherSuiteIndex if ciphersuite unknown
324    */
325   static size_t lookupCiphersuiteByOpenSSLName(const char *opensslCipherSuiteName);
326 
327   /** returns a ciphersuite name in RFC 2246 (TLS) form
328    *  @param idx index, must be < getNumberOfCipherSuites()
329    *  @return ciphersuite name
330    */
331   static const char *getTLSCipherSuiteName(size_t idx);
332 
333   /** returns a ciphersuite name in OpenSSL form
334    *  @param idx index, must be < getNumberOfCipherSuites()
335    *  @return ciphersuite name
336    */
337   static const char *getOpenSSLCipherSuiteName(size_t idx);
338 
339   /** returns the minimum SSL/TLS version required for the ciphersuite with the given index
340    *  @param idx index, must be < getNumberOfCipherSuites()
341    *  @return minimum SSL/TLS version required
342    */
343   static DcmTLSCipherProtocolVersion getCipherSuiteProtocolVersion(size_t idx);
344 
345   /** returns the key exchange protocol used by the ciphersuite with the given index
346    *  @param idx index, must be < getNumberOfCipherSuites()
347    *  @return key exchange protocol
348    */
349   static DcmTLSCipherKeyExchange getCipherSuiteKeyExchange(size_t idx);
350 
351   /** returns the authentication algorithm used by the ciphersuite with the given index
352    *  @param idx index, must be < getNumberOfCipherSuites()
353    *  @return authentication algorithm
354    */
355   static DcmTLSCipherAuthentication getCipherSuiteAuthentication(size_t idx);
356 
357   /** returns the encryption algorithm used by  the ciphersuite with the given index
358    *  @param idx index, must be < getNumberOfCipherSuites()
359    *  @return minimum SSL/TLS version required
360    */
361   static DcmTLSCipherEncryption getCipherSuiteEncryption(size_t idx);
362 
363   /** returns the message authentication code (MAC) algorithm used by  the ciphersuite with the given index
364    *  @param idx index, must be < getNumberOfCipherSuites()
365    *  @return message authentication code (MAC) algorithm
366    */
367   static DcmTLSCipherMAC getCipherSuiteMAC(size_t idx);
368 
369   /** returns the symmetric key size used by the ciphersuite with the given index
370    *  @param idx index, must be < getNumberOfCipherSuites()
371    *  @return symmetric key size, in bits
372    */
373   static size_t getCipherSuiteKeySize(size_t idx);
374 
375   /** returns the effective symmetric key size (i.e. security level) of the ciphersuite with the given index.
376    *  BCP 195 (2015) recommends that no ciphersuites with an effective key size of less than 112 bits should be used anymore with TLS.
377    *  @param idx index, must be < getNumberOfCipherSuites()
378    *  @return effective symmetric key size, in bits
379    */
380   static size_t getCipherSuiteEffectiveKeySize(size_t idx);
381 
382   /** look up the name of the given security profile
383    *  @return name of security profile, never NULL.
384    */
385   static const char *lookupProfileName(DcmTLSSecurityProfile profile);
386 
387   /// constant returned by findOpenSSLCipherSuiteName() if ciphersuite name is unknown
388   static const size_t unknownCipherSuiteIndex;
389 
390 private:
391 
392   /// private undefined copy constructor
393   DcmTLSCiphersuiteHandler(const DcmTLSCiphersuiteHandler&);
394 
395   /// private undefined assignment operator
396   DcmTLSCiphersuiteHandler& operator=(const DcmTLSCiphersuiteHandler&);
397 
398   /** determine the set of ciphersuites that are supported both by DCMTK
399    *  and the OpenSSL library we are currently using
400    */
401   void determineSupportedCiphers();
402 
403   /** add ciphersuite by name, print error if unsupported
404    *  @param name ciphersuite name in RFC 2246 form
405    *  @return TCS_ok if successful, an error code otherwise
406    */
407   DcmTransportLayerStatus addRequiredCipherSuite(const char *name);
408 
409   /** add 3DES ciphersuite, print warning if unsupported
410    */
411   void addOptional3DESCipherSuite();
412 
413   /// current list of ciphersuites
414   OFVector<size_t> ciphersuiteList;
415 
416   /// currently selected DICOM TLS security profile
417   DcmTLSSecurityProfile currentProfile;
418 
419   /// indicator whether TLS 1.3 is enabled or disabled for the current profile
420    OFBool tls13_enabled;
421 
422   /** an array of booleans indicating which ciphersuites known to DCMTK are
423    *  actually supported by the OpenSSL library we are using.
424    */
425   OFBool *ciphersuiteSupported;
426 };
427 
428 #endif /* WITH_OPENSSL */
429 #endif /* TLSCIPHR_H */
430