Coverage Report

Created: 2025-07-11 07:47

/src/PcapPlusPlus/Packet++/header/SSLHandshake.h
Line
Count
Source (jump to first uncovered line)
1
#pragma once
2
3
#include <utility>
4
#include "SSLCommon.h"
5
#include "PointerVector.h"
6
#include "Asn1Codec.h"
7
8
/// @file
9
/// See detailed explanation of the TLS/SSL protocol support in PcapPlusPlus in SSLLayer.h
10
11
/// @namespace pcpp
12
/// @brief The main namespace for the PcapPlusPlus lib
13
namespace pcpp
14
{
15
  /// @class SSLCipherSuite
16
  /// Represents a cipher-suite and enables access all information about it such as all algorithms it encapsulates,
17
  /// its ID (as appears in the client-hello or server-hello messages),
18
  /// its name (e.g "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA") etc. PcapPlusPlus contains static instances of this type
19
  /// for all known cipher-suites and enables access to them through name or ID (see getCipherSuiteByID() and
20
  /// getCipherSuiteByName() ). List of cipher-suite was taken from here:
21
  /// http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
22
  class SSLCipherSuite
23
  {
24
  public:
25
    /// A c'tor for this class, should never be used by a user
26
    /// @param[in] id Cipher-suite ID
27
    /// @param[in] keyExAlg Key-exchange algorithm used in this cipher-suite
28
    /// @param[in] authAlg Authentication algorithm used in this cipher-suite
29
    /// @param[in] symKeyAlg Symmetric key algorithm used in this cipher-suite
30
    /// @param[in] MACAlg MAC algorithm used in this cipher-suite
31
    /// @param[in] name String representation of this cipher-suite
32
    SSLCipherSuite(uint16_t id, SSLKeyExchangeAlgorithm keyExAlg, SSLAuthenticationAlgorithm authAlg,
33
                   SSLSymetricEncryptionAlgorithm symKeyAlg, SSLHashingAlgorithm MACAlg, const char* name)
34
658
        : m_Id(id), m_KeyExAlg(keyExAlg), m_AuthAlg(authAlg), m_SymKeyAlg(symKeyAlg), m_MACAlg(MACAlg), m_Name(name)
35
658
    {}
36
37
    /// @return Cipher-suite ID
38
    uint16_t getID() const
39
0
    {
40
0
      return m_Id;
41
0
    }
42
43
    /// @return String representation of this cipher-suite
44
    std::string asString() const
45
0
    {
46
0
      return m_Name;
47
0
    }
48
49
    /// @return Key-exchange algorithm used in this cipher-suite
50
    SSLKeyExchangeAlgorithm getKeyExchangeAlg() const
51
0
    {
52
0
      return m_KeyExAlg;
53
0
    }
54
55
    /// @return Authentication algorithm used in this cipher-suite
56
    SSLAuthenticationAlgorithm getAuthAlg() const
57
0
    {
58
0
      return m_AuthAlg;
59
0
    }
60
61
    /// @return Symmetric key algorithm used in this cipher-suite
62
    SSLSymetricEncryptionAlgorithm getSymKeyAlg() const
63
0
    {
64
0
      return m_SymKeyAlg;
65
0
    }
66
67
    /// @return MAC algorithm used in this cipher-suite
68
    SSLHashingAlgorithm getMACAlg() const
69
0
    {
70
0
      return m_MACAlg;
71
0
    }
72
73
    /// A static method that returns a cipher-suite instance by ID
74
    /// @param[in] id Cipher-suite ID
75
    /// @return A cipher-suite instance matching this ID or nullptr if ID not found
76
    static SSLCipherSuite* getCipherSuiteByID(uint16_t id);
77
78
    ///  A static method that returns a cipher-suite instance by name
79
    ///  @param[in] name Cipher-suite name (e.g "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA")
80
    ///  @return A cipher-suite instance matching this name or nullptr if name not found
81
    static SSLCipherSuite* getCipherSuiteByName(std::string name);
82
83
  private:
84
    uint16_t m_Id;
85
    SSLKeyExchangeAlgorithm m_KeyExAlg;
86
    SSLAuthenticationAlgorithm m_AuthAlg;
87
    SSLSymetricEncryptionAlgorithm m_SymKeyAlg;
88
    SSLHashingAlgorithm m_MACAlg;
89
    std::string m_Name;
90
  };
91
92
  /// @class SSLExtension
93
  /// Represents a SSL/TLS extension. This is a base class that can represent any type of extension. Inherited classes
94
  /// may contain parsing logic for specific extensions. This class provides capabilities such as getting the
95
  /// extension type, length and viewing the extension data as raw (byte array)
96
  class SSLExtension
97
  {
98
  public:
99
    /// C'tor for this class
100
    /// @param[in] data The raw data for the extension
101
    explicit SSLExtension(uint8_t* data);
102
103
140k
    virtual ~SSLExtension() = default;
104
105
    /// @return The type of the extension as enum
106
    SSLExtensionType getType() const;
107
108
    /// @return The type of the extension as a numeric value
109
    uint16_t getTypeAsInt() const;
110
111
    /// @return The length of the extension data in bytes (not including the type and length fields)
112
    uint16_t getLength() const;
113
114
    /// @return The total length of the extension, including type and length fields and the extension data field
115
    uint16_t getTotalLength() const;
116
117
    /// @return A pointer to the raw data of the extension
118
    uint8_t* getData() const;
119
120
  protected:
121
    /// @struct SSLExtensionStruct
122
    /// Represents the common fields of the extension
123
    struct SSLExtensionStruct
124
    {
125
      /// Extension type
126
      uint16_t extensionType;
127
      /// Extension length
128
      uint16_t extensionDataLength;
129
      /// Extension data as raw (byte array)
130
      uint8_t extensionData[];
131
    };
132
133
    uint8_t* m_RawData;
134
135
    SSLExtensionStruct* getExtensionStruct() const
136
332k
    {
137
332k
      return reinterpret_cast<SSLExtensionStruct*>(m_RawData);
138
332k
    }
139
  };
140
141
  /// @class SSLServerNameIndicationExtension
142
  /// Represents SSL/TLS Server Name Indication extension. Inherits from SSLExtension and add parsing of the hostname
143
  /// written in the extension data
144
  class SSLServerNameIndicationExtension : public SSLExtension
145
  {
146
  public:
147
    /// C'tor for this class
148
    /// @param[in] data The raw data for the extension
149
62.5k
    explicit SSLServerNameIndicationExtension(uint8_t* data) : SSLExtension(data)
150
62.5k
    {}
151
152
    /// @return The hostname written in the extension data
153
    std::string getHostName() const;
154
  };
155
156
  /// @class SSLSupportedVersionsExtension
157
  /// Represents TLS Supported Versions extension. Inherits from SSLExtension and adds parsing of the list
158
  /// of supported versions mentioned in the extension data
159
  class SSLSupportedVersionsExtension : public SSLExtension
160
  {
161
  public:
162
    /// C'tor for this class
163
    /// @param[in] data The raw data for the extension
164
2.41k
    explicit SSLSupportedVersionsExtension(uint8_t* data) : SSLExtension(data)
165
2.41k
    {}
166
167
    /// @return The list of supported versions mentioned in the extension data
168
    std::vector<SSLVersion> getSupportedVersions() const;
169
  };
170
171
  /// @class TLSSupportedGroupsExtension
172
  /// Represents TLS Supported Groups extension. Inherits from SSLExtension and adds parsing of the
173
  /// supported groups (Elliptic Curves) mentioned in the extension data
174
  class TLSSupportedGroupsExtension : public SSLExtension
175
  {
176
  public:
177
    /// C'tor for this class
178
    /// @param[in] data The raw data for the extension
179
5.69k
    explicit TLSSupportedGroupsExtension(uint8_t* data) : SSLExtension(data)
180
5.69k
    {}
181
182
    /// @return A vector of the supported groups (also known as "Elliptic Curves")
183
    std::vector<uint16_t> getSupportedGroups() const;
184
  };
185
186
  /// @class TLSECPointFormatExtension
187
  /// Represents TLS EC (Elliptic Curves) Point Format extension. Inherits from SSLExtension and adds parsing of the
188
  /// EC point formats mentioned in the extension data
189
  class TLSECPointFormatExtension : public SSLExtension
190
  {
191
  public:
192
    /// C'tor for this class
193
    /// @param[in] data The raw data for the extension
194
6.23k
    explicit TLSECPointFormatExtension(uint8_t* data) : SSLExtension(data)
195
6.23k
    {}
196
197
    /// @return A vector of the elliptic curves point formats
198
    std::vector<uint8_t> getECPointFormatList() const;
199
  };
200
201
  /// @class SSLx509Certificate
202
  /// Represents a x509v3 certificate. the SSLCertificateMessage class returns an instance of this class as the
203
  /// certificate. Currently this class doesn't do much as it doesn't parse the certificate. It only acts as container
204
  /// to the raw data and returns general info as data as raw, length, etc. In the future I may add full parsing of
205
  /// the certificate
206
  class SSLx509Certificate
207
  {
208
  public:
209
    /// C'tor for this class
210
    /// @param[in] data The raw data of the certificate
211
    /// @param[in] dataLen The length in bytes of the raw data
212
    /// @param[in] allDataExists Certificate messages usually spread on more than 1 packet. So a certificate is
213
    /// likely to split between 2 packets or more. This field indicates whether the raw data contains all
214
    /// certificate data of just a part of it
215
    SSLx509Certificate(uint8_t* data, size_t dataLen, bool allDataExists)
216
6.38k
        : m_Data(data), m_DataLen(dataLen), m_AllDataExists(allDataExists)
217
6.38k
    {}
218
219
    /// @return A pointer to the raw data
220
    uint8_t* getData() const
221
0
    {
222
0
      return m_Data;
223
0
    }
224
225
    /// @return Raw data length
226
    size_t getDataLength() const
227
0
    {
228
0
      return m_DataLen;
229
0
    }
230
231
    /// @return The root ASN.1 record of the certificate data. All of the certificate data will be under this
232
    /// record. If the Root ASN.1 record is malformed, an exception is thrown
233
    Asn1SequenceRecord* getRootAsn1Record();
234
235
    /// Certificate messages usually spread on more than 1 packet. So a certificate is likely to split between 2
236
    /// packets or more. This method provides an indication whether all certificate data exists or only part of it
237
    /// @return True if this data contains all certificate data, false otherwise
238
    bool allDataExists() const
239
0
    {
240
0
      return m_AllDataExists;
241
0
    }
242
243
  private:
244
    std::unique_ptr<Asn1Record> m_Asn1Record;
245
    uint8_t* m_Data;
246
    size_t m_DataLen;
247
    bool m_AllDataExists;
248
  };
249
250
  class SSLHandshakeLayer;
251
252
  /// @class SSLHandshakeMessage
253
  /// A base class for SSL/TLS handshake messages. This is an abstract class and cannot be instantiated. SSL/TLS
254
  /// handshake messages are contained in SSLHandshakeLayer, meaning a SSLHandshakeLayer instance can contain one or
255
  /// more SSLHandshakeMessage instances. For example: one SSLHandshakeLayer may contain a server-hello, certificate,
256
  /// server-key-exchange, and server-hello-done messages (although it's not such a common case, most handshake layers
257
  /// contain 1 handshake message only)
258
  class SSLHandshakeMessage
259
  {
260
  public:
261
51.4k
    virtual ~SSLHandshakeMessage() = default;
262
263
    /// A factory method for creating instances of handshake messages from raw data
264
    /// @param[in] data The raw data containing 1 handshake message
265
    /// @param[in] dataLen Raw data length in bytes
266
    /// @param[in] container A pointer to the SSLHandshakeLayer instance which will contain the created message.
267
    /// This parameter is required because the handshake message includes a pointer to its container
268
    static SSLHandshakeMessage* createHandshakeMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container);
269
270
    /// @return The handshake message type
271
    virtual SSLHandshakeType getHandshakeType() const;
272
273
    /// @return The handshake message length in bytes. Notice that sometimes the handshake message is divided
274
    /// between several packets, in this case this method will return the length of part of the message in the
275
    /// current packet
276
    virtual size_t getMessageLength() const;
277
278
    /// @return True if current packet contains the entire message or false otherwise. This method is important
279
    /// because sometimes handshake messages are divided in consequent packets (happens a lot in certificate
280
    /// messages which usually contain several KB of data which is larger than standard packet size, so the message
281
    /// is divided between several packets)
282
    virtual bool isMessageComplete() const;
283
284
    /// @return A pointer to the SSLHandshakeLayer instance containing this message
285
    SSLHandshakeLayer* getContainingLayer() const
286
0
    {
287
0
      return m_Container;
288
0
    }
289
290
    /// @return A string representation of the message type (e.g "Client Hello message")
291
    virtual std::string toString() const = 0;
292
293
  protected:
294
    SSLHandshakeMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container);
295
296
    uint8_t* m_Data;
297
    size_t m_DataLen;
298
    SSLHandshakeLayer* m_Container;
299
  };
300
301
  /// @class SSLClientHelloMessage
302
  /// Represents a client-hello message (type 1). Inherits from SSLHandshakeMessage and adds parsing of all fields
303
  /// of this message including the message extensions, cipher-suite list, etc.
304
  class SSLClientHelloMessage : public SSLHandshakeMessage
305
  {
306
  public:
307
    /// @struct ClientHelloTLSFingerprint
308
    /// A struct that contains all the elements needed for creating a Client Hello TLS fingerprinting:
309
    /// TLS version, a list of Cipher Suite IDs, a list of Extensions IDs, a list of support groups and a list of
310
    /// EC point formats.
311
    /// You can read more about this in SSLClientHelloMessage#generateTLSFingerprint().
312
    /// This struct contains methods to extract the TLS fingerprint itself: toString() and toMD5()
313
    struct ClientHelloTLSFingerprint
314
    {
315
      /// TLS version
316
      uint16_t tlsVersion;
317
      /// A list of Cipher Suite IDs
318
      std::vector<uint16_t> cipherSuites;
319
      /// A list of extension IDs
320
      std::vector<uint16_t> extensions;
321
      /// A list of Suppotred Groups taken from the "supported groups" TLS extension (if exist in the message)
322
      std::vector<uint16_t> supportedGroups;
323
      /// A list of EC point formats taken from the "EC point formats" TLS extension (if exist in the message)
324
      std::vector<uint8_t> ecPointFormats;
325
326
      /// @return A string representing the TLS fingerprint, for example:
327
      /// <b>771,4866-4867-4865-255,0-11-10-35-22-23-13-43-45-51,29-23-30-25-24,0-1-2</b>
328
      ///
329
      /// This string has the following format:
330
      /// <b>TLSVersion,CipherSuiteIDs,ExtensionIDs,SupportedGroups,ECPointFormats</b>
331
      ///
332
      /// The extension IDs, supported groups and EC point formats are each separated by a "-".
333
      /// If the message doesn't include the "supported groups" or "EC point formats" extensions, they will be
334
      /// replaced by an empty string for example: <b>771,4866-4867-4865-255,0-11-10-35-22-23-13-43-45-51,,</b>
335
      std::string toString();
336
337
      /// @return An MD5 hash of the string generated by toString()
338
      std::string toMD5();
339
340
      /// @return A pair of the string and MD5 hash (string is first, MD5 is second).
341
      /// If you want both this method is more efficient than calling toString() and toMD5() separately
342
      std::pair<std::string, std::string> toStringAndMD5();
343
    };
344
345
    /// C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and shouldn't
346
    /// be used by a user
347
    /// @param[in] data The message as raw data
348
    /// @param[in] dataLen Message raw data length in bytes
349
    /// @param[in] container The SSL handshake layer which shall contain this message
350
    SSLClientHelloMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container);
351
352
10.0k
    ~SSLClientHelloMessage() override = default;
353
354
    /// @return A struct containing common fields for client-hello and server-hello messages. Notice this points
355
    /// directly to the data, so every change will change the actual packet data
356
    ssl_tls_client_server_hello* getClientHelloHeader() const
357
3.78k
    {
358
3.78k
      return reinterpret_cast<ssl_tls_client_server_hello*>(m_Data);
359
3.78k
    }
360
361
    /// @return Handshake SSL/TLS version (notice it may be different than SSLLayer#getRecordVersion(). Each
362
    /// client-hello or server-hello message has both record version and handshake version and they may differ from
363
    /// one another)
364
    SSLVersion getHandshakeVersion() const;
365
366
    /// @return Session ID length in bytes. If server-hello message doesn't include session ID 0 will be returned
367
    uint8_t getSessionIDLength() const;
368
369
    /// @return Session ID as byte array. If server-hello message doesn't include session ID nullptr will be
370
    /// returned
371
    uint8_t* getSessionID() const;
372
373
    /// @return The number of cipher-suites included in this message
374
    int getCipherSuiteCount() const;
375
376
    /// Get a pointer to a cipher-suite by index. The cipher-suites are numbered according to their order of
377
    /// appearance in the message. If index is out of bounds (less than 0 or larger than total amount of cipher
378
    /// suites) nullptr will be returned. nullptr will also be returned if the cipher-suite ID is unknown. If you
379
    /// still want to get the cipher-suite ID you can use getCipherSuiteID()
380
    /// @param[in] index The index of the cipher-suite to return
381
    /// @return The pointer to the cipher-suite object or nullptr if index is out of bounds
382
    SSLCipherSuite* getCipherSuite(int index) const;
383
384
    /// Get the cipher-suite ID by index. This method just parses the ID from the client-hello message and returns
385
    /// it. To get more information on the cipher-suite you can use the getCipherSuite() method
386
    /// @param[in] index The index of the cipher-suite to return
387
    /// @param[out] isValid Set to "true" if parsing succeeded and the return value is valid or "false" if:
388
    /// (1) the index is out-of-bounds (less than 0 or larger than total amount of cipher suites) or (2) the parsing
389
    /// failed. If the value is "false" the return value can be ignored
390
    /// @return The cipher-suite ID if "isValid" is set to "true". If "isValid" is set to "false" the return value
391
    /// can be ignored
392
    uint16_t getCipherSuiteID(int index, bool& isValid) const;
393
394
    /// @return The value of the compression method byte
395
    uint8_t getCompressionMethodsValue() const;
396
397
    /// @return The number of extensions in this message
398
    int getExtensionCount() const;
399
400
    /// @return The size (in bytes) of all extensions data in this message. Extracted from the "extensions length"
401
    /// field
402
    uint16_t getExtensionsLength() const;
403
404
    /// Get a pointer to an extension by index. The extensions are numbered according to their order of appearance
405
    /// in the message. If index is out of bounds (less than 0 or larger than total amount of extensions) nullptr
406
    /// will be returned
407
    /// @param[in] index The index of the extension to return
408
    /// @return The pointer to the extension or nullptr if index is out of bounds
409
    SSLExtension* getExtension(int index) const;
410
411
    /// Get a pointer to an extension by numeric type field. Every extension has a 2-byte numeric value representing
412
    /// its type (for example: renegotiation info extension type is 0x1ff). This method gets the type and returns a
413
    /// pointer to the extension object
414
    /// @param[in] type The 2-byte numeric type of the extension
415
    /// @return A pointer to the extension object of nullptr if this type doesn't exist in this message
416
    SSLExtension* getExtensionOfType(uint16_t type) const;
417
418
    /// Get a pointer to an extension by its enum type
419
    /// @param[in] type The type of extension to return
420
    /// @return A pointer to the extension object or nullptr if this type doesn't exist in this message
421
    SSLExtension* getExtensionOfType(SSLExtensionType type) const;
422
423
    /// Get a pointer to an extension by its class type. This is a templated method that is used with the type of
424
    /// the requested extension and returns the first extension instance of this type
425
    /// @return A pointer to the extension object or nullptr if this extension type doesn't exist in this message
426
    template <class TExtension> TExtension* getExtensionOfType() const;
427
428
    /// TLS fingerprinting is a way to identify client applications using the details in the TLS Client Hello
429
    /// packet. It was initially introduced by Lee Brotherston in his 2015 research:
430
    /// <https://blog.squarelemon.com/tls-fingerprinting/> This implementation of TLS fingerprint is a C++ version
431
    /// of Salesforce's JA3 open source project (originally written in Python and Zeek):
432
    /// <https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967>
433
    /// @return A SSLClientHelloMessage#ClientHelloTLSFingerprint struct that contains all the elements needed for
434
    /// creating a TLS fingerprint out of this Client Hello message. This struct has also methods to extract the TLS
435
    /// fingerprint itself in a string or MD5 formats
436
    ClientHelloTLSFingerprint generateTLSFingerprint() const;
437
438
    // implement abstract methods
439
440
    std::string toString() const override;
441
442
  private:
443
    PointerVector<SSLExtension> m_ExtensionList;
444
  };
445
446
  /// @class SSLServerHelloMessage
447
  /// Represents SSL/TLS server-hello message (type 2). Inherits from SSLHandshakeMessage and adds parsing of all
448
  /// fields of this message including the message extensions, cipher-suite, etc.
449
  class SSLServerHelloMessage : public SSLHandshakeMessage
450
  {
451
  public:
452
    /// @struct ServerHelloTLSFingerprint
453
    /// A struct that contains all the elements needed for creating a Server Hello TLS fingerprinting:
454
    /// TLS version, Cipher Suite ID, and a list of Extensions IDs.
455
    /// You can read more about this in SSLServerHelloMessage#generateTLSFingerprint().
456
    /// This struct contains methods to extract the TLS fingerprint itself: toString() and toMD5()
457
    struct ServerHelloTLSFingerprint
458
    {
459
      /// TLS version
460
      uint16_t tlsVersion;
461
      /// Cipher Suite ID
462
      uint16_t cipherSuite;
463
      /// A list of extension IDs
464
      std::vector<uint16_t> extensions;
465
466
      /// @return A string representing the TLS fingerprint, for example: <b>771,49195,65281-16-11</b>
467
      ///
468
      /// This string has the following format: <b>TLSVersion,Cipher,Extensions</b>
469
      ///
470
      /// The extension ID are separated with a "-"
471
      std::string toString();
472
473
      /// @return An MD5 hash of the string generated by toString()
474
      std::string toMD5();
475
476
      /// @return A pair of the string and MD5 hash (string is first, MD5 is second).
477
      /// If you want both this method is more efficient than calling toString() and toMD5() separately
478
      std::pair<std::string, std::string> toStringAndMD5();
479
    };
480
481
    /// C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and shouldn't
482
    /// be used by a user
483
    /// @param[in] data The message as raw data
484
    /// @param[in] dataLen Message raw data length in bytes
485
    /// @param[in] container The SSL handshake layer which shall contain this message
486
    SSLServerHelloMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container);
487
488
9.45k
    ~SSLServerHelloMessage() override = default;
489
490
    /// @return A struct containing common fields for client-hello and server-hello messages. Notice this points
491
    /// directly to the data, so every change will change the actual packet data
492
    ssl_tls_client_server_hello* getServerHelloHeader() const
493
2.67k
    {
494
2.67k
      return reinterpret_cast<ssl_tls_client_server_hello*>(m_Data);
495
2.67k
    }
496
497
    /// @return Handshake SSL/TLS version (notice it may be different than SSLLayer#getRecordVersion(). Each
498
    /// client-hello or server-hello message has both record version and handshake version and they may differ from
499
    /// one another).
500
    ///
501
    /// <b>NOTE:</b> for TLS 1.3 the handshake version written in ssl_tls_client_server_hello::handshakeVersion is
502
    /// still TLS 1.2, so a special check is made here see if a SupportedVersions extension exists and if so extract
503
    /// the version from it. This is the most straight-forward way to detect TLS 1.3.
504
    SSLVersion getHandshakeVersion() const;
505
506
    /// @return Session ID length in bytes. If server-hello message doesn't include session ID 0 will be returned
507
    uint8_t getSessionIDLength() const;
508
509
    /// @return Session ID as byte array. If server-hello message doesn't include session ID nullptr will be
510
    /// returned
511
    uint8_t* getSessionID() const;
512
513
    /// @return A pointer to the cipher suite encapsulated in this message (server-hello message contains one
514
    /// cipher-suite, the one that will be used to for encryption between client and server). May return nullptr
515
    /// if the parsing of the message failed or the cipher-suite ID is unknown. If you still want to get the
516
    /// cipher-suite ID you can use the getCipherSuiteID() method
517
    SSLCipherSuite* getCipherSuite() const;
518
519
    /// Get the cipher-suite ID. This method just parses the ID from the server-hello message and returns it.
520
    /// To get more information on the cipher-suite you can use the getCipherSuite() method
521
    /// @param[out] isValid Set to "true" if parsing succeeded and the return value is valid or "false" otherwise.
522
    /// If the value is "false" the return value can be ignored
523
    /// @return The cipher-suite ID if "isValid" is set to "true". If "isValid" is set to "false" the return value
524
    /// can be ignored
525
    uint16_t getCipherSuiteID(bool& isValid) const;
526
527
    /// @return The value of the compression method byte
528
    uint8_t getCompressionMethodsValue() const;
529
530
    /// @return The number of extensions in this message
531
    int getExtensionCount() const;
532
533
    /// @return The size (in bytes) of all extensions data in this message. Extracted from the "extensions length"
534
    /// field
535
    uint16_t getExtensionsLength() const;
536
537
    /// Get a pointer to an extension by index. The extensions are numbered according to their order of appearance
538
    /// in the message. If index is out of bounds (less than 0 or larger than total amount of extensions) nullptr
539
    /// will be returned
540
    /// @param[in] index The index of the extension to return
541
    /// @return The pointer to the extension or nullptr if index is out of bounds
542
    SSLExtension* getExtension(int index) const;
543
544
    /// Get a pointer to an extension by numeric type field. Every extension has a 2-byte numeric value representing
545
    /// its type (for example: renegotiation info extension type is 0x1ff). This method gets the type and returns a
546
    /// pointer to the extension object
547
    /// @param[in] type The 2-byte numeric type of the extension
548
    /// @return A pointer to the extension object of nullptr if this type doesn't exist in this message
549
    SSLExtension* getExtensionOfType(uint16_t type) const;
550
551
    /// Get a pointer to an extension by its enum type
552
    /// @param[in] type The type of extension to return
553
    /// @return A pointer to the extension object or nullptr if this type doesn't exist in this message
554
    SSLExtension* getExtensionOfType(SSLExtensionType type) const;
555
556
    /// Get a pointer to an extension by its class type. This is a templated method that is used with the type of
557
    /// the requested extension and returns the first extension instance of this type
558
    /// @return A pointer to the extension object or nullptr if this extension type doesn't exist in this message
559
    template <class TExtension> TExtension* getExtensionOfType() const;
560
561
    /// ServerHello TLS fingerprinting is a way to fingerprint TLS Server Hello messages. In conjunction with
562
    /// ClientHello TLS fingerprinting it can assist in identifying specific client-server communication (for
563
    /// example: a malware connecting to its backend server).
564
    /// ServerHello TLS fingerprinting was introduced in Salesforce's JA3S open source project:
565
    /// <https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967>
566
    /// This implementation is a C++ version of Salesforce's JAS3 (originally written in Python and Zeek)
567
    /// @return A SSLServerHelloMessage#ServerHelloTLSFingerprint struct that contains all the elements needed for
568
    /// creating a TLS fingerprint out of this Server Hello message. This struct has also methods to extract the TLS
569
    /// fingerprint itself in a string or MD5 formats
570
    ServerHelloTLSFingerprint generateTLSFingerprint() const;
571
572
    // implement abstract methods
573
574
    std::string toString() const override;
575
576
  private:
577
    PointerVector<SSLExtension> m_ExtensionList;
578
  };
579
580
  /// @class SSLCertificateMessage
581
  /// Represents SSL/TLS certificate message (type 11). Inherits from SSLHandshakeMessage and adds parsing
582
  /// functionality such as extracting the certificates data. Notice that in most cases this message is spread over
583
  /// more than 1 packet as its size is too big for a single packet. So SSLCertificateMessage instance will be created
584
  /// just for the first part of the message - the one encapsulated in the first packet. Other parts (encapsulated in
585
  /// the following packets) won't be recognized as SSLCertificateMessage messages
586
  class SSLCertificateMessage : public SSLHandshakeMessage
587
  {
588
  public:
589
    /// C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be
590
    /// used by a user
591
    /// @param[in] data The message as raw data
592
    /// @param[in] dataLen Message raw data length in bytes
593
    /// @param[in] container The SSL handshake layer which shall contain this message
594
    SSLCertificateMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container);
595
596
2.07k
    ~SSLCertificateMessage() override = default;
597
598
    /// @return The number of certificates encapsulated in this message (as written in the 'length' field of the
599
    /// message). Notice that because the message may spread over several packets, not all certificates will
600
    /// necessarily be in this packet. So, for example, there may be a case where this method return 3 (message
601
    /// contains 3 certificates) but this message actually contains only 1 certificate as the other 2 are spread
602
    /// over the other packets
603
    int getNumOfCertificates() const;
604
605
    /// Get a certificate by index
606
    /// @param[in] index The index of the certificate to retrieve
607
    /// @return A pointer to the certificate object. Notice that if index < 0 or index > num of certificates
608
    /// encapsulated in current packet a nullptr value will be returned
609
    SSLx509Certificate* getCertificate(int index) const;
610
611
    // implement abstract methods
612
613
    std::string toString() const override;
614
615
  private:
616
    PointerVector<SSLx509Certificate> m_CertificateList;
617
  };
618
619
  /// @class SSLHelloRequestMessage
620
  /// Represents SSL/TLS hello-request message (type 0). This message has no additional payload except for the common
621
  /// payload described in SSLHandshakeMessage
622
  class SSLHelloRequestMessage : public SSLHandshakeMessage
623
  {
624
  public:
625
    /// C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be
626
    /// used by a user
627
    /// @param[in] data The message as raw data
628
    /// @param[in] dataLen Message raw data length in bytes
629
    /// @param[in] container The SSL handshake layer which shall contain this message
630
    SSLHelloRequestMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container)
631
8.57k
        : SSLHandshakeMessage(data, dataLen, container)
632
8.57k
    {}
633
634
    ~SSLHelloRequestMessage() override = default;
635
636
    // implement abstract methods
637
638
    std::string toString() const override;
639
  };
640
641
  /// @class SSLServerKeyExchangeMessage
642
  /// Represents SSL/TLS server-key-exchange message (type 12). Inherits from SSLHandshakeMessage and adds parsing
643
  /// functionality such as getting the server key exchange params as raw data (parsing of this may be added in the
644
  /// future)
645
  class SSLServerKeyExchangeMessage : public SSLHandshakeMessage
646
  {
647
  public:
648
    /// C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be
649
    /// used by a user
650
    /// @param[in] data The message as raw data
651
    /// @param[in] dataLen Message raw data length in bytes
652
    /// @param[in] container The SSL handshake layer which shall contain this message
653
    SSLServerKeyExchangeMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container)
654
970
        : SSLHandshakeMessage(data, dataLen, container)
655
970
    {}
656
657
    ~SSLServerKeyExchangeMessage() override = default;
658
659
    /// @return A pointer to the raw data of the server key exchange params. Currently this data can only returned
660
    /// as raw, parsing may be added in the future. Notice that if the message is spread over more than 1 packet in
661
    /// a way params doesn't exist in the first packet, nullptr will be returned
662
    uint8_t* getServerKeyExchangeParams() const;
663
664
    /// @return The size of the params field. Notice that if the message is spread over more than 1 packet in a way
665
    /// the ssl_tls_handshake_layer cannot be parsed from the packet, 0 will be returned. Also, if only part of the
666
    /// params exist in current packet (and the rest are on consequent packets), the size that will be returned is
667
    /// the size of the part that exists in the current packet (and not total size of params)
668
    size_t getServerKeyExchangeParamsLength() const;
669
670
    // implement abstract methods
671
672
    std::string toString() const override;
673
  };
674
675
  /// @class SSLClientKeyExchangeMessage
676
  /// Represents SSL/TLS client-key-exchange message (type 16). Inherits from SSLHandshakeMessage and adds parsing
677
  /// functionality such as getting the server key exchange params as raw data (parsing of this may be added in the
678
  /// future)
679
  class SSLClientKeyExchangeMessage : public SSLHandshakeMessage
680
  {
681
  public:
682
    /// C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be
683
    /// used by a user
684
    /// @param[in] data The message as raw data
685
    /// @param[in] dataLen Message raw data length in bytes
686
    /// @param[in] container The SSL handshake layer which shall contain this message
687
    SSLClientKeyExchangeMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container)
688
1.25k
        : SSLHandshakeMessage(data, dataLen, container)
689
1.25k
    {}
690
691
    ~SSLClientKeyExchangeMessage() override = default;
692
693
    /// @return A pointer to the raw data of the server key exchange params. Currently this data can only be
694
    /// returned as raw, parsing may be added in the future. Notice that if the message is spread over more than 1
695
    /// packet in a way params doesn't exist in the first packet, nullptr will be returned
696
    uint8_t* getClientKeyExchangeParams() const;
697
698
    /// @return The size of the params field. Notice that if the message is spread over more than 1 packet in a way
699
    /// the ssl_tls_handshake_layer cannot be parsed from the packet, 0 will be returned. Also, if only part of the
700
    /// params exist in current packet (and the rest are on consequent packets), the size that will be returned is
701
    /// the size of the part that exists in the current packet (and not the total size of params)
702
    size_t getClientKeyExchangeParamsLength() const;
703
704
    // implement abstract methods
705
706
    std::string toString() const override;
707
  };
708
709
  /// @class SSLCertificateRequestMessage
710
  /// Represents SSL/TLS certificate-request message (type 13). Inherits from SSLHandshakeMessage and adds parsing
711
  /// functionality such as retrieving client certificate types and authority data
712
  class SSLCertificateRequestMessage : public SSLHandshakeMessage
713
  {
714
  public:
715
    /// C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be
716
    /// used by a user
717
    /// @param[in] data The message as raw data
718
    /// @param[in] dataLen Message raw data length in bytes
719
    /// @param[in] container The SSL handshake layer which shall contain this message
720
    SSLCertificateRequestMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container);
721
722
2.89k
    ~SSLCertificateRequestMessage() override = default;
723
724
    /// @return A reference to a vector containing all client certificate types exist in this message
725
    std::vector<SSLClientCertificateType>& getCertificateTypes();
726
727
    /// @return A pointer to the certificate authority data as raw data (byte array). Parsing of this data may be
728
    /// added in the future. Notice that if this message is spread over several packets in a way none of the
729
    /// certificate authority data exists in this packet, nullptr will be returned
730
    uint8_t* getCertificateAuthorityData() const;
731
732
    /// @return The length of certificate authority data returned by getCertificateAuthorityData(). Notice that if
733
    /// this message is spread over several packets in a way none of certificate authority data exists in the
734
    /// current packet, 0 will be returned. Also, if some of the data exists in the consequent packets, the length
735
    /// that will be returned is the length of data exists in the current packet only (and not the total length)
736
    size_t getCertificateAuthorityLength() const;
737
738
    // implement abstract methods
739
740
    std::string toString() const override;
741
742
  private:
743
    std::vector<SSLClientCertificateType> m_ClientCertificateTypes;
744
  };
745
746
  /// @class SSLServerHelloDoneMessage
747
  /// Represents SSL/TLS server-hello-done message (type 14). This message has no additional payload except for the
748
  /// common payload described in SSLHandshakeMessage
749
  class SSLServerHelloDoneMessage : public SSLHandshakeMessage
750
  {
751
  public:
752
    /// C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be
753
    /// used by a user
754
    /// @param[in] data The message as raw data
755
    /// @param[in] dataLen Message raw data length in bytes
756
    /// @param[in] container The SSL handshake layer which shall contain this message
757
    SSLServerHelloDoneMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container)
758
549
        : SSLHandshakeMessage(data, dataLen, container)
759
549
    {}
760
761
    ~SSLServerHelloDoneMessage() override = default;
762
763
    // implement abstract methods
764
765
    std::string toString() const override;
766
  };
767
768
  /// @class SSLCertificateVerifyMessage
769
  /// Represents SSL/TLS certificate-verify message (type 15). Inherits from SSLHandshakeMessage and adds parsing
770
  /// functionality such as retrieving signed hash data as raw data (parsing may be added in the future)
771
  /// @todo This message type wasn't tested in unit-tests
772
  class SSLCertificateVerifyMessage : public SSLHandshakeMessage
773
  {
774
  public:
775
    /// C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be
776
    /// used by a user
777
    /// @param[in] data The message as raw data
778
    /// @param[in] dataLen Message raw data length in bytes
779
    /// @param[in] container The SSL handshake layer which shall contain this message
780
    SSLCertificateVerifyMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container)
781
        : SSLHandshakeMessage(data, dataLen, container)
782
0
    {}
783
784
    ~SSLCertificateVerifyMessage() override = default;
785
786
    /// @return A pointer to the signed hash data as raw data (byte array). Parsing of this data may be added
787
    /// in the future. Notice that if this message is spread over several packets in a way none of the signed hash
788
    /// data exists in this packet, nullptr will be returned
789
    uint8_t* getSignedHash() const;
790
791
    /// @return The length of signed hash data returned by getSignedHash(). Notice that if this message is spread
792
    /// over several packets in a way none of this data exists in the current packet, 0 will be returned. Also, if
793
    /// some of the data exists in the consequent packets, the length that will be returned will be the length of
794
    /// data exists in the current packet only (and not the total length)
795
    size_t getSignedHashLength() const;
796
797
    // implement abstract methods
798
799
    std::string toString() const override;
800
  };
801
802
  /// @class SSLFinishedMessage
803
  /// Represents SSL/TLS finished message (type 20). Inherits from SSLHandshakeMessage and adds parsing
804
  /// functionality such as retrieving signed hash data as raw data (parsing may be added in the future)
805
  /// @todo This message type wasn't tested in unit-tests
806
  class SSLFinishedMessage : public SSLHandshakeMessage
807
  {
808
  public:
809
    /// C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be
810
    /// used by a user
811
    /// @param[in] data The message as raw data
812
    /// @param[in] dataLen Message raw data length in bytes
813
    /// @param[in] container The SSL handshake layer which shall contain this message
814
    SSLFinishedMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container)
815
510
        : SSLHandshakeMessage(data, dataLen, container)
816
510
    {}
817
818
    ~SSLFinishedMessage() override = default;
819
820
    /// @return A pointer to the signed hash data as raw data (byte array). Parsing of this data may be added
821
    /// in the future. Notice that if this message is spread over several packets in a way none of the signed hash
822
    /// data exists in this packet, nullptr will be returned
823
    uint8_t* getSignedHash() const;
824
825
    /// @return The length of signed hash data returned by getSignedHash(). Notice that if the message is spread
826
    /// over several packets in a way none of this data exists in the current packet, 0 will be returned. Also, if
827
    /// some of the data exists in the consequent packets, the length that will be returned will be the length of
828
    /// data exists in the current packet only (and not the total length)
829
    size_t getSignedHashLength() const;
830
831
    // implement abstract methods
832
833
    std::string toString() const override;
834
  };
835
836
  /// @class SSLNewSessionTicketMessage
837
  /// Represents SSL/TLS new-session-ticket message (type 4). Inherits from SSLHandshakeMessage and adds parsing
838
  /// functionality such as retrieving session ticket data as raw data (parsing may be added in the future)
839
  class SSLNewSessionTicketMessage : public SSLHandshakeMessage
840
  {
841
  public:
842
    /// C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be
843
    /// used by a user
844
    /// @param[in] data The message as raw data
845
    /// @param[in] dataLen Message raw data length in bytes
846
    /// @param[in] container The SSL handshake layer which shall contain this message
847
    SSLNewSessionTicketMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container)
848
537
        : SSLHandshakeMessage(data, dataLen, container)
849
537
    {}
850
851
    ~SSLNewSessionTicketMessage() override = default;
852
853
    /// @return A pointer to the session ticket data as raw data (byte array). Parsing of this data may be added
854
    /// in the future. Notice that if this message is spread over several packets in a way none of the signed hash
855
    /// data exists in current packet, nullptr will be returned
856
    uint8_t* getSessionTicketData() const;
857
858
    /// @return The length of session ticket data returned by getSessionTicketData(). Notice that if this message is
859
    /// spread over several packets in a way none of this data exists in the current packet, 0 will be returned.
860
    /// Also, if some of the data exist in the consequent packets, the length that will be returned will be the
861
    /// length of the data existing in the current packet only (and not the total length)
862
    size_t getSessionTicketDataLength() const;
863
864
    // implement abstract methods
865
866
    std::string toString() const override;
867
  };
868
869
  /// @class SSLUnknownMessage
870
  /// Represents an unknown type of message or an encrypted message that PcapPlusPlus can't determine its type. In
871
  /// these cases length can't always be determined from the message itself (especially if the message is encrypted),
872
  /// so the length of this message will always be the size counted from message start until the end of the layer
873
  class SSLUnknownMessage : public SSLHandshakeMessage
874
  {
875
  public:
876
    /// C'tor for this class. Currently only in use in SSLHandshakeMessage::createHandshakeMessage() and should be
877
    /// used by a user
878
    /// @param[in] data The message as raw data
879
    /// @param[in] dataLen Message raw data length in bytes
880
    /// @param[in] container The SSL handshake layer which shall contain this message
881
    SSLUnknownMessage(uint8_t* data, size_t dataLen, SSLHandshakeLayer* container)
882
14.5k
        : SSLHandshakeMessage(data, dataLen, container)
883
14.5k
    {}
884
885
    ~SSLUnknownMessage() override = default;
886
887
    // implement virtual and abstract methods
888
889
    /// @return Always ::SSL_HANDSHAKE_UNKNOWN (overridden from SSLHandshakeMessage)
890
    SSLHandshakeType getHandshakeType() const override;
891
892
    /// @return The length of the data from message start until the end of the layer. Since it's an unknown type
893
    /// or an encrypted message the length parsed from the message can't be guaranteed to be the correct length.
894
    /// That's why the length returned is the size until the end of the layer
895
    size_t getMessageLength() const override;
896
897
    std::string toString() const override;
898
  };
899
900
  template <class TExtension> TExtension* SSLClientHelloMessage::getExtensionOfType() const
901
7.56k
  {
902
7.56k
    size_t vecSize = m_ExtensionList.size();
903
59.7k
    for (size_t i = 0; i < vecSize; i++)
904
55.7k
    {
905
55.7k
      SSLExtension* curElem = const_cast<SSLExtension*>(m_ExtensionList.at(i));
906
55.7k
      if (dynamic_cast<TExtension*>(curElem) != nullptr)
907
3.53k
        return static_cast<TExtension*>(curElem);
908
55.7k
    }
909
910
4.03k
    return nullptr;
911
7.56k
  }
pcpp::SSLServerNameIndicationExtension* pcpp::SSLClientHelloMessage::getExtensionOfType<pcpp::SSLServerNameIndicationExtension>() const
Line
Count
Source
901
1.89k
  {
902
1.89k
    size_t vecSize = m_ExtensionList.size();
903
3.76k
    for (size_t i = 0; i < vecSize; i++)
904
3.13k
    {
905
3.13k
      SSLExtension* curElem = const_cast<SSLExtension*>(m_ExtensionList.at(i));
906
3.13k
      if (dynamic_cast<TExtension*>(curElem) != nullptr)
907
1.26k
        return static_cast<TExtension*>(curElem);
908
3.13k
    }
909
910
627
    return nullptr;
911
1.89k
  }
pcpp::SSLSupportedVersionsExtension* pcpp::SSLClientHelloMessage::getExtensionOfType<pcpp::SSLSupportedVersionsExtension>() const
Line
Count
Source
901
1.89k
  {
902
1.89k
    size_t vecSize = m_ExtensionList.size();
903
25.8k
    for (size_t i = 0; i < vecSize; i++)
904
24.0k
    {
905
24.0k
      SSLExtension* curElem = const_cast<SSLExtension*>(m_ExtensionList.at(i));
906
24.0k
      if (dynamic_cast<TExtension*>(curElem) != nullptr)
907
105
        return static_cast<TExtension*>(curElem);
908
24.0k
    }
909
910
1.78k
    return nullptr;
911
1.89k
  }
pcpp::TLSSupportedGroupsExtension* pcpp::SSLClientHelloMessage::getExtensionOfType<pcpp::TLSSupportedGroupsExtension>() const
Line
Count
Source
901
1.89k
  {
902
1.89k
    size_t vecSize = m_ExtensionList.size();
903
15.2k
    for (size_t i = 0; i < vecSize; i++)
904
14.4k
    {
905
14.4k
      SSLExtension* curElem = const_cast<SSLExtension*>(m_ExtensionList.at(i));
906
14.4k
      if (dynamic_cast<TExtension*>(curElem) != nullptr)
907
1.05k
        return static_cast<TExtension*>(curElem);
908
14.4k
    }
909
910
840
    return nullptr;
911
1.89k
  }
pcpp::TLSECPointFormatExtension* pcpp::SSLClientHelloMessage::getExtensionOfType<pcpp::TLSECPointFormatExtension>() const
Line
Count
Source
901
1.89k
  {
902
1.89k
    size_t vecSize = m_ExtensionList.size();
903
14.8k
    for (size_t i = 0; i < vecSize; i++)
904
14.1k
    {
905
14.1k
      SSLExtension* curElem = const_cast<SSLExtension*>(m_ExtensionList.at(i));
906
14.1k
      if (dynamic_cast<TExtension*>(curElem) != nullptr)
907
1.11k
        return static_cast<TExtension*>(curElem);
908
14.1k
    }
909
910
779
    return nullptr;
911
1.89k
  }
912
913
  template <class TExtension> TExtension* SSLServerHelloMessage::getExtensionOfType() const
914
4.16k
  {
915
4.16k
    size_t vecSize = m_ExtensionList.size();
916
9.48k
    for (size_t i = 0; i < vecSize; i++)
917
6.23k
    {
918
6.23k
      SSLExtension* curElem = const_cast<SSLExtension*>(m_ExtensionList.at(i));
919
6.23k
      if (dynamic_cast<TExtension*>(curElem) != nullptr)
920
911
        return static_cast<TExtension*>(curElem);
921
6.23k
    }
922
923
3.25k
    return nullptr;
924
4.16k
  }
pcpp::SSLServerNameIndicationExtension* pcpp::SSLServerHelloMessage::getExtensionOfType<pcpp::SSLServerNameIndicationExtension>() const
Line
Count
Source
914
1.38k
  {
915
1.38k
    size_t vecSize = m_ExtensionList.size();
916
2.79k
    for (size_t i = 0; i < vecSize; i++)
917
1.78k
    {
918
1.78k
      SSLExtension* curElem = const_cast<SSLExtension*>(m_ExtensionList.at(i));
919
1.78k
      if (dynamic_cast<TExtension*>(curElem) != nullptr)
920
377
        return static_cast<TExtension*>(curElem);
921
1.78k
    }
922
923
1.01k
    return nullptr;
924
1.38k
  }
pcpp::SSLSupportedVersionsExtension* pcpp::SSLServerHelloMessage::getExtensionOfType<pcpp::SSLSupportedVersionsExtension>() const
Line
Count
Source
914
2.77k
  {
915
2.77k
    size_t vecSize = m_ExtensionList.size();
916
6.68k
    for (size_t i = 0; i < vecSize; i++)
917
4.44k
    {
918
4.44k
      SSLExtension* curElem = const_cast<SSLExtension*>(m_ExtensionList.at(i));
919
4.44k
      if (dynamic_cast<TExtension*>(curElem) != nullptr)
920
534
        return static_cast<TExtension*>(curElem);
921
4.44k
    }
922
923
2.24k
    return nullptr;
924
2.77k
  }
925
}  // namespace pcpp