Coverage Report

Created: 2025-10-10 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/src/X509Decoder.cpp
Line
Count
Source
1
#include "X509Decoder.h"
2
#include "Asn1Codec.h"
3
#include "GeneralUtils.h"
4
#include "PemCodec.h"
5
#include "json.hpp"
6
#include <fstream>
7
#include <unordered_map>
8
9
namespace pcpp
10
{
11
  std::string X509Algorithm::toString() const
12
0
  {
13
0
    switch (m_Value)
14
0
    {
15
0
    case SHA1:
16
0
      return "SHA1";
17
0
    case SHA256:
18
0
      return "SHA256";
19
0
    case SHA384:
20
0
      return "SHA384";
21
0
    case SHA512:
22
0
      return "SHA512";
23
0
    case MD5:
24
0
      return "MD5";
25
0
    case RSA:
26
0
      return "RSA";
27
0
    case RSAWithSHA1:
28
0
      return "RSAWithSHA1";
29
0
    case RSAWithSHA256:
30
0
      return "RSAWithSHA256";
31
0
    case RSAWithSHA384:
32
0
      return "RSAWithSHA384";
33
0
    case RSAWithSHA512:
34
0
      return "RSAWithSHA512";
35
0
    case RSAPSS:
36
0
      return "RSAPSS";
37
0
    case ECDSA:
38
0
      return "ECDSA";
39
0
    case ECDSAWithSHA1:
40
0
      return "ECDSAWithSHA1";
41
0
    case ECDSAWithSHA256:
42
0
      return "ECDSAWithSHA256";
43
0
    case ECDSAWithSHA384:
44
0
      return "ECDSAWithSHA384";
45
0
    case ECDSAWithSHA512:
46
0
      return "ECDSAWithSHA512";
47
0
    case ED25519:
48
0
      return "ED25519";
49
0
    case ED448:
50
0
      return "ED448";
51
0
    case DSA:
52
0
      return "DSA";
53
0
    case DSAWithSHA1:
54
0
      return "DSAWithSHA1";
55
0
    case DSAWithSHA256:
56
0
      return "DSAWithSHA256";
57
0
    case DiffieHellman:
58
0
      return "DiffieHellman";
59
0
    case Unknown:
60
0
    default:
61
0
      return "Unknown";
62
0
    }
63
0
  }
64
65
  std::string X509Algorithm::getOidValue() const
66
0
  {
67
0
    switch (m_Value)
68
0
    {
69
0
    case SHA1:
70
0
      return "1.3.14.3.2.26";
71
0
    case SHA256:
72
0
      return "2.16.840.1.101.3.4.2.1";
73
0
    case SHA384:
74
0
      return "2.16.840.1.101.3.4.2.2";
75
0
    case SHA512:
76
0
      return "2.16.840.1.101.3.4.2.3";
77
0
    case MD5:
78
0
      return "1.2.840.113549.2.5";
79
80
0
    case RSA:
81
0
      return "1.2.840.113549.1.1.1";
82
0
    case RSAWithSHA1:
83
0
      return "1.2.840.113549.1.1.5";
84
0
    case RSAWithSHA256:
85
0
      return "1.2.840.113549.1.1.11";
86
0
    case RSAWithSHA384:
87
0
      return "1.2.840.113549.1.1.12";
88
0
    case RSAWithSHA512:
89
0
      return "1.2.840.113549.1.1.13";
90
0
    case RSAPSS:
91
0
      return "1.2.840.113549.1.1.10";
92
93
0
    case ECDSA:
94
0
      return "1.2.840.10045.2.1";
95
0
    case ECDSAWithSHA1:
96
0
      return "1.2.840.10045.4.1";
97
0
    case ECDSAWithSHA256:
98
0
      return "1.2.840.10045.4.3.2";
99
0
    case ECDSAWithSHA384:
100
0
      return "1.2.840.10045.4.3.3";
101
0
    case ECDSAWithSHA512:
102
0
      return "1.2.840.10045.4.3.4";
103
104
0
    case ED25519:
105
0
      return "1.3.101.112";
106
0
    case ED448:
107
0
      return "1.3.101.113";
108
109
0
    case DSA:
110
0
      return "1.2.840.10040.4.1";
111
0
    case DSAWithSHA1:
112
0
      return "1.2.840.10040.4.3";
113
0
    case DSAWithSHA256:
114
0
      return "2.16.840.1.101.3.4.3.2";
115
116
0
    case DiffieHellman:
117
0
      return "1.2.840.113549.1.3.1";
118
119
0
    case Unknown:
120
0
    default:
121
0
      return "0.0";
122
0
    }
123
0
  }
124
125
  static const std::unordered_map<std::string, X509Algorithm::Value> X509AlgorithmOidMap = {
126
    { "1.3.14.3.2.26",          X509Algorithm::SHA1            },
127
    { "2.16.840.1.101.3.4.2.1", X509Algorithm::SHA256          },
128
    { "2.16.840.1.101.3.4.2.2", X509Algorithm::SHA384          },
129
    { "2.16.840.1.101.3.4.2.3", X509Algorithm::SHA512          },
130
    { "1.2.840.113549.2.5",     X509Algorithm::MD5             },
131
    { "1.2.840.113549.1.1.1",   X509Algorithm::RSA             },
132
    { "1.2.840.113549.1.1.5",   X509Algorithm::RSAWithSHA1     },
133
    { "1.2.840.113549.1.1.11",  X509Algorithm::RSAWithSHA256   },
134
    { "1.2.840.113549.1.1.12",  X509Algorithm::RSAWithSHA384   },
135
    { "1.2.840.113549.1.1.13",  X509Algorithm::RSAWithSHA512   },
136
    { "1.2.840.113549.1.1.10",  X509Algorithm::RSAPSS          },
137
    { "1.2.840.10045.2.1",      X509Algorithm::ECDSA           },
138
    { "1.2.840.10045.4.1",      X509Algorithm::ECDSAWithSHA1   },
139
    { "1.2.840.10045.4.3.2",    X509Algorithm::ECDSAWithSHA256 },
140
    { "1.2.840.10045.4.3.3",    X509Algorithm::ECDSAWithSHA384 },
141
    { "1.2.840.10045.4.3.4",    X509Algorithm::ECDSAWithSHA512 },
142
    { "1.2.840.10040.4.1",      X509Algorithm::DSA             },
143
    { "1.2.840.10040.4.3",      X509Algorithm::DSAWithSHA1     },
144
    { "2.16.840.1.101.3.4.3.2", X509Algorithm::DSAWithSHA256   },
145
    { "1.3.101.112",            X509Algorithm::ED25519         },
146
    { "1.3.101.113",            X509Algorithm::ED448           },
147
    { "1.2.840.113549.1.3.1",   X509Algorithm::DiffieHellman   }
148
  };
149
150
  X509Algorithm X509Algorithm::fromOidValue(const Asn1ObjectIdentifier& value)
151
0
  {
152
0
    std::string oidStringValue = value.toString();
153
154
0
    auto it = X509AlgorithmOidMap.find(oidStringValue);
155
0
    if (it != X509AlgorithmOidMap.end())
156
0
    {
157
0
      return { it->second };
158
0
    }
159
160
0
    return { Unknown };
161
0
  }
162
163
  std::string X520DistinguishedName::toString() const
164
0
  {
165
0
    switch (m_Value)
166
0
    {
167
0
    case CommonName:
168
0
      return "CommonName";
169
0
    case Surname:
170
0
      return "Surname";
171
0
    case SerialNumber:
172
0
      return "SerialNumber";
173
0
    case Country:
174
0
      return "Country";
175
0
    case Locality:
176
0
      return "Locality";
177
0
    case StateOrProvince:
178
0
      return "StateOrProvinceName";
179
0
    case Organization:
180
0
      return "Organization";
181
0
    case OrganizationalUnit:
182
0
      return "OrganizationalUnit";
183
0
    case Title:
184
0
      return "Title";
185
0
    case GivenName:
186
0
      return "GivenName";
187
0
    case Initials:
188
0
      return "Initials";
189
0
    case Pseudonym:
190
0
      return "Pseudonym";
191
0
    case GenerationQualifier:
192
0
      return "GenerationQualifier";
193
0
    case DnQualifier:
194
0
      return "DnQualifier";
195
0
    case DomainComponent:
196
0
      return "DomainComponent";
197
0
    case EmailAddress:
198
0
      return "EmailAddress";
199
0
    case PostalCode:
200
0
      return "PostalCode";
201
0
    case StreetAddress:
202
0
      return "StreetAddress";
203
0
    case BusinessCategory:
204
0
      return "BusinessCategory";
205
0
    case Unknown:
206
0
    default:
207
0
      return "Unknown";
208
0
    }
209
0
  }
210
211
  std::string X520DistinguishedName::getShortName() const
212
0
  {
213
0
    switch (m_Value)
214
0
    {
215
0
    case CommonName:
216
0
      return "CN";
217
0
    case Surname:
218
0
      return "SN";
219
0
    case SerialNumber:
220
0
      return "SERIALNUMBER";
221
0
    case Country:
222
0
      return "C";
223
0
    case Locality:
224
0
      return "L";
225
0
    case StateOrProvince:
226
0
      return "ST";
227
0
    case Organization:
228
0
      return "O";
229
0
    case OrganizationalUnit:
230
0
      return "OU";
231
0
    case Title:
232
0
      return "T";
233
0
    case GivenName:
234
0
      return "G";
235
0
    case Initials:
236
0
      return "Initials";
237
0
    case Pseudonym:
238
0
      return "Pseudonym";
239
0
    case GenerationQualifier:
240
0
      return "GENERATION";
241
0
    case DnQualifier:
242
0
      return "dnQualifier";
243
0
    case DomainComponent:
244
0
      return "DC";
245
0
    case EmailAddress:
246
0
      return "E";
247
0
    case PostalCode:
248
0
      return "postalCode";
249
0
    case StreetAddress:
250
0
      return "STREET";
251
0
    case BusinessCategory:
252
0
      return "businessCategory";
253
0
    case Unknown:
254
0
    default:
255
0
      return "Unknown";
256
0
    }
257
0
  }
258
259
  std::string X520DistinguishedName::getOidValue() const
260
0
  {
261
0
    switch (m_Value)
262
0
    {
263
0
    case CommonName:
264
0
      return "2.5.4.3";
265
0
    case Surname:
266
0
      return "2.5.4.4";
267
0
    case SerialNumber:
268
0
      return "2.5.4.5";
269
0
    case Country:
270
0
      return "2.5.4.6";
271
0
    case Locality:
272
0
      return "2.5.4.7";
273
0
    case StateOrProvince:
274
0
      return "2.5.4.8";
275
0
    case Organization:
276
0
      return "2.5.4.10";
277
0
    case OrganizationalUnit:
278
0
      return "2.5.4.11";
279
0
    case Title:
280
0
      return "2.5.4.12";
281
0
    case GivenName:
282
0
      return "2.5.4.42";
283
0
    case Initials:
284
0
      return "2.5.4.43";
285
0
    case GenerationQualifier:
286
0
      return "2.5.4.44";
287
0
    case DnQualifier:
288
0
      return "2.5.4.46";
289
0
    case Pseudonym:
290
0
      return "2.5.4.65";
291
0
    case DomainComponent:
292
0
      return "0.9.2342.19200300.100.1.25";
293
0
    case EmailAddress:
294
0
      return "1.2.840.113549.1.9.1";
295
0
    case PostalCode:
296
0
      return "2.5.4.17";
297
0
    case StreetAddress:
298
0
      return "2.5.4.9";
299
0
    case BusinessCategory:
300
0
      return "2.5.4.15";
301
0
    case Unknown:
302
0
    default:
303
0
      return "0.0";
304
0
    }
305
0
  }
306
307
  static const std::unordered_map<std::string, X520DistinguishedName::Value> X520DistinguishedNameOidMap = {
308
    { "2.5.4.3",                    X520DistinguishedName::CommonName          },
309
    { "2.5.4.4",                    X520DistinguishedName::Surname             },
310
    { "2.5.4.5",                    X520DistinguishedName::SerialNumber        },
311
    { "2.5.4.6",                    X520DistinguishedName::Country             },
312
    { "2.5.4.7",                    X520DistinguishedName::Locality            },
313
    { "2.5.4.8",                    X520DistinguishedName::StateOrProvince     },
314
    { "2.5.4.10",                   X520DistinguishedName::Organization        },
315
    { "2.5.4.11",                   X520DistinguishedName::OrganizationalUnit  },
316
    { "2.5.4.12",                   X520DistinguishedName::Title               },
317
    { "2.5.4.42",                   X520DistinguishedName::GivenName           },
318
    { "2.5.4.43",                   X520DistinguishedName::Initials            },
319
    { "2.5.4.44",                   X520DistinguishedName::GenerationQualifier },
320
    { "2.5.4.46",                   X520DistinguishedName::DnQualifier         },
321
    { "2.5.4.65",                   X520DistinguishedName::Pseudonym           },
322
    { "0.9.2342.19200300.100.1.25", X520DistinguishedName::DomainComponent     },
323
    { "1.2.840.113549.1.9.1",       X520DistinguishedName::EmailAddress        },
324
    { "2.5.4.17",                   X520DistinguishedName::PostalCode          },
325
    { "2.5.4.9",                    X520DistinguishedName::StreetAddress       },
326
    { "2.5.4.15",                   X520DistinguishedName::BusinessCategory    }
327
  };
328
329
  X520DistinguishedName X520DistinguishedName::fromOidValue(const Asn1ObjectIdentifier& value)
330
0
  {
331
0
    std::string oidStringValue = value.toString();
332
333
0
    auto it = X520DistinguishedNameOidMap.find(oidStringValue);
334
0
    if (it != X520DistinguishedNameOidMap.end())
335
0
    {
336
0
      return { it->second };
337
0
    }
338
339
0
    return { Unknown };
340
0
  }
341
342
  std::string X509ExtensionType::toString() const
343
0
  {
344
0
    switch (m_Value)
345
0
    {
346
0
    case BasicConstraints:
347
0
      return "BasicConstraints";
348
0
    case KeyUsage:
349
0
      return "KeyUsage";
350
0
    case ExtendedKeyUsage:
351
0
      return "ExtendedKeyUsage";
352
0
    case SubjectKeyIdentifier:
353
0
      return "SubjectKeyIdentifier";
354
0
    case AuthorityKeyIdentifier:
355
0
      return "AuthorityKeyIdentifier";
356
0
    case SubjectAltName:
357
0
      return "SubjectAltName";
358
0
    case IssuerAltName:
359
0
      return "IssuerAltName";
360
0
    case CrlDistributionPoints:
361
0
      return "CRLDistributionPoints";
362
0
    case AuthorityInfoAccess:
363
0
      return "AuthorityInfoAccess";
364
0
    case CertificatePolicies:
365
0
      return "CertificatePolicies";
366
0
    case PolicyMappings:
367
0
      return "PolicyMappings";
368
0
    case PolicyConstraints:
369
0
      return "PolicyConstraints";
370
0
    case NameConstraints:
371
0
      return "NameConstraints";
372
0
    case InhibitAnyPolicy:
373
0
      return "InhibitAnyPolicy";
374
0
    case CTPrecertificateSCTs:
375
0
      return "CTPrecertificateSCTs";
376
0
    case SubjectInfoAccess:
377
0
      return "SubjectInfoAccess";
378
0
    case FreshestCRL:
379
0
      return "FreshestCRL";
380
0
    case TLSFeature:
381
0
      return "TLSFeature";
382
0
    case OcspNoCheck:
383
0
      return "OcspNoCheck";
384
0
    case SubjectDirectoryAttributes:
385
0
      return "SubjectDirectoryAttributes";
386
0
    case Unknown:
387
0
    default:
388
0
      return "Unknown";
389
0
    }
390
0
  }
391
392
  std::string X509ExtensionType::getOidValue() const
393
0
  {
394
0
    switch (m_Value)
395
0
    {
396
0
    case BasicConstraints:
397
0
      return "2.5.29.19";
398
0
    case KeyUsage:
399
0
      return "2.5.29.15";
400
0
    case ExtendedKeyUsage:
401
0
      return "2.5.29.37";
402
0
    case SubjectKeyIdentifier:
403
0
      return "2.5.29.14";
404
0
    case AuthorityKeyIdentifier:
405
0
      return "2.5.29.35";
406
0
    case SubjectAltName:
407
0
      return "2.5.29.17";
408
0
    case IssuerAltName:
409
0
      return "2.5.29.18";
410
0
    case CrlDistributionPoints:
411
0
      return "2.5.29.31";
412
0
    case AuthorityInfoAccess:
413
0
      return "1.3.6.1.5.5.7.1.1";
414
0
    case CertificatePolicies:
415
0
      return "2.5.29.32";
416
0
    case PolicyMappings:
417
0
      return "2.5.29.33";
418
0
    case PolicyConstraints:
419
0
      return "2.5.29.36";
420
0
    case NameConstraints:
421
0
      return "2.5.29.30";
422
0
    case InhibitAnyPolicy:
423
0
      return "2.5.29.54";
424
0
    case CTPrecertificateSCTs:
425
0
      return "1.3.6.1.4.1.11129.2.4.2";
426
0
    case SubjectInfoAccess:
427
0
      return "1.3.6.1.5.5.7.1.11";
428
0
    case FreshestCRL:
429
0
      return "2.5.29.46";
430
0
    case TLSFeature:
431
0
      return "1.3.6.1.5.5.7.1.24";
432
0
    case OcspNoCheck:
433
0
      return "1.3.6.1.5.5.7.48.1.5";
434
0
    case SubjectDirectoryAttributes:
435
0
      return "2.5.29.9";
436
0
    case Unknown:
437
0
    default:
438
0
      return "0.0";
439
0
    }
440
0
  }
441
442
  static const std::unordered_map<std::string, X509ExtensionType::Value> X509ExtensionTypeOidMap = {
443
    { "2.5.29.19",               X509ExtensionType::BasicConstraints           },
444
    { "2.5.29.15",               X509ExtensionType::KeyUsage                   },
445
    { "2.5.29.37",               X509ExtensionType::ExtendedKeyUsage           },
446
    { "2.5.29.14",               X509ExtensionType::SubjectKeyIdentifier       },
447
    { "2.5.29.35",               X509ExtensionType::AuthorityKeyIdentifier     },
448
    { "2.5.29.17",               X509ExtensionType::SubjectAltName             },
449
    { "2.5.29.18",               X509ExtensionType::IssuerAltName              },
450
    { "2.5.29.31",               X509ExtensionType::CrlDistributionPoints      },
451
    { "1.3.6.1.5.5.7.1.1",       X509ExtensionType::AuthorityInfoAccess        },
452
    { "2.5.29.32",               X509ExtensionType::CertificatePolicies        },
453
    { "2.5.29.33",               X509ExtensionType::PolicyMappings             },
454
    { "2.5.29.36",               X509ExtensionType::PolicyConstraints          },
455
    { "2.5.29.30",               X509ExtensionType::NameConstraints            },
456
    { "2.5.29.54",               X509ExtensionType::InhibitAnyPolicy           },
457
    { "1.3.6.1.4.1.11129.2.4.2", X509ExtensionType::CTPrecertificateSCTs       },
458
    { "1.3.6.1.5.5.7.1.11",      X509ExtensionType::SubjectInfoAccess          },
459
    { "2.5.29.46",               X509ExtensionType::FreshestCRL                },
460
    { "1.3.6.1.5.5.7.1.24",      X509ExtensionType::TLSFeature                 },
461
    { "1.3.6.1.5.5.7.48.1.5",    X509ExtensionType::OcspNoCheck                },
462
    { "2.5.29.9",                X509ExtensionType::SubjectDirectoryAttributes },
463
  };
464
465
  X509ExtensionType X509ExtensionType::fromOidValue(const Asn1ObjectIdentifier& value)
466
0
  {
467
0
    std::string oidStr = value.toString();
468
469
0
    auto it = X509ExtensionTypeOidMap.find(oidStr);
470
0
    if (it != X509ExtensionTypeOidMap.end())
471
0
      return { it->second };
472
473
0
    return { Unknown };
474
0
  }
475
476
  template <class Asn1RecordType>
477
  static Asn1RecordType* castRecordAs(Asn1Record* record, const std::string& fieldName)
478
0
  {
479
0
    try
480
0
    {
481
0
      return record->castAs<Asn1RecordType>();
482
0
    }
483
0
    catch (const std::bad_cast&)
484
0
    {
485
0
      throw std::runtime_error("Invalid X509 certificate data: " + fieldName);
486
0
    }
487
0
  }
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1IntegerRecord* pcpp::castRecordAs<pcpp::Asn1IntegerRecord>(pcpp::Asn1Record*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1ObjectIdentifierRecord* pcpp::castRecordAs<pcpp::Asn1ObjectIdentifierRecord>(pcpp::Asn1Record*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1SetRecord* pcpp::castRecordAs<pcpp::Asn1SetRecord>(pcpp::Asn1Record*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1TimeRecord* pcpp::castRecordAs<pcpp::Asn1TimeRecord>(pcpp::Asn1Record*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1BitStringRecord* pcpp::castRecordAs<pcpp::Asn1BitStringRecord>(pcpp::Asn1Record*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1BooleanRecord* pcpp::castRecordAs<pcpp::Asn1BooleanRecord>(pcpp::Asn1Record*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1OctetStringRecord* pcpp::castRecordAs<pcpp::Asn1OctetStringRecord>(pcpp::Asn1Record*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1SequenceRecord* pcpp::castRecordAs<pcpp::Asn1SequenceRecord>(pcpp::Asn1Record*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1ConstructedRecord* pcpp::castRecordAs<pcpp::Asn1ConstructedRecord>(pcpp::Asn1Record*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
488
489
  template <class Asn1RecordType>
490
  static Asn1RecordType* getSubRecordAndCast(Asn1ConstructedRecord* record, int index, const std::string& fieldName)
491
0
  {
492
0
    try
493
0
    {
494
0
      return castRecordAs<Asn1RecordType>(record->getSubRecords().at(index), fieldName);
495
0
    }
496
0
    catch (const std::out_of_range&)
497
0
    {
498
0
      throw std::runtime_error("Invalid X509 certificate data: " + fieldName);
499
0
    }
500
0
  }
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1IntegerRecord* pcpp::getSubRecordAndCast<pcpp::Asn1IntegerRecord>(pcpp::Asn1ConstructedRecord*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1SequenceRecord* pcpp::getSubRecordAndCast<pcpp::Asn1SequenceRecord>(pcpp::Asn1ConstructedRecord*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1ObjectIdentifierRecord* pcpp::getSubRecordAndCast<pcpp::Asn1ObjectIdentifierRecord>(pcpp::Asn1ConstructedRecord*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1TimeRecord* pcpp::getSubRecordAndCast<pcpp::Asn1TimeRecord>(pcpp::Asn1ConstructedRecord*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1BitStringRecord* pcpp::getSubRecordAndCast<pcpp::Asn1BitStringRecord>(pcpp::Asn1ConstructedRecord*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1BooleanRecord* pcpp::getSubRecordAndCast<pcpp::Asn1BooleanRecord>(pcpp::Asn1ConstructedRecord*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1OctetStringRecord* pcpp::getSubRecordAndCast<pcpp::Asn1OctetStringRecord>(pcpp::Asn1ConstructedRecord*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: X509Decoder.cpp:pcpp::Asn1ConstructedRecord* pcpp::getSubRecordAndCast<pcpp::Asn1ConstructedRecord>(pcpp::Asn1ConstructedRecord*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
501
502
  std::string X509SerialNumber::toString(const std::string& delimiter) const
503
0
  {
504
0
    if (delimiter.empty())
505
0
    {
506
0
      return m_SerialNumber;
507
0
    }
508
509
    // Add delimiter
510
0
    std::string result;
511
0
    result.reserve(m_SerialNumber.length() + delimiter.size() * (m_SerialNumber.length() / 2 - 1));
512
513
0
    for (size_t i = 0; i < m_SerialNumber.length(); ++i)
514
0
    {
515
0
      result += m_SerialNumber[i];
516
      // Add a delimiter after every two characters, except for the very last pair
517
0
      if ((i + 1) % 2 == 0 && i + 1 < m_SerialNumber.length())
518
0
      {
519
0
        result += delimiter;
520
0
      }
521
0
    }
522
0
    return result;
523
0
  }
524
525
  std::string X509Timestamp::toString(const std::string& format, const std::string& timezone,
526
                                      bool includeMilliseconds) const
527
0
  {
528
0
    return m_Record->getValueAsString(format, timezone, includeMilliseconds);
529
0
  }
530
531
  std::chrono::system_clock::time_point X509Timestamp::getTimestamp(const std::string& timezone) const
532
0
  {
533
0
    return m_Record->getValue(timezone);
534
0
  }
535
536
  std::string X509Key::toString(const std::string& delimiter) const
537
0
  {
538
0
    std::ostringstream result;
539
0
    bool first = true;
540
541
0
    for (const auto& byte : m_Key)
542
0
    {
543
0
      if (!first)
544
0
      {
545
0
        result << delimiter;
546
0
      }
547
0
      result << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(byte);
548
0
      first = false;
549
0
    }
550
551
0
    return result.str();
552
0
  }
553
554
  const std::vector<uint8_t>& X509Key::getBytes() const
555
0
  {
556
0
    return m_Key;
557
0
  }
558
559
  namespace X509Internal
560
  {
561
    X509Version X509VersionRecord::getVersion() const
562
0
    {
563
0
      auto intValue = getSubRecordAndCast<Asn1IntegerRecord>(m_Root, 0, "Version")->getIntValue<uint8_t>();
564
0
      if (intValue > 3)
565
0
      {
566
0
        throw std::runtime_error("Invalid X509 version value: " + std::to_string(intValue));
567
0
      }
568
569
0
      return static_cast<X509Version>(intValue);
570
0
    }
571
572
    bool X509VersionRecord::isValidVersionRecord(const Asn1Record* record)
573
0
    {
574
0
      return record->getTagClass() == Asn1TagClass::ContextSpecific && record->getTagType() == 0 &&
575
0
             record->isConstructed();
576
0
    }
577
578
    Asn1Record* X509RelativeDistinguishedName::getRecord(int index) const
579
0
    {
580
0
      auto attributeTypeAndValue = getSubRecordAndCast<Asn1SequenceRecord>(m_Root, 0, "RDN");
581
0
      try
582
0
      {
583
0
        return attributeTypeAndValue->getSubRecords().at(index);
584
0
      }
585
0
      catch (const std::out_of_range&)
586
0
      {
587
0
        throw std::runtime_error("Invalid X509 certificate data: RDN");
588
0
      }
589
0
    }
590
591
    X520DistinguishedName X509RelativeDistinguishedName::getType() const
592
0
    {
593
0
      auto oidRecord = castRecordAs<Asn1ObjectIdentifierRecord>(getRecord(typeOffset), "RDN Type");
594
0
      return X520DistinguishedName::fromOidValue(oidRecord->getValue());
595
0
    }
596
597
    std::string X509RelativeDistinguishedName::getValue() const
598
0
    {
599
0
      auto valueRecord = getRecord(valueOffset);
600
0
      switch (valueRecord->getUniversalTagType())
601
0
      {
602
0
      case Asn1UniversalTagType::PrintableString:
603
0
      {
604
0
        return valueRecord->castAs<Asn1PrintableStringRecord>()->getValue();
605
0
      }
606
0
      case Asn1UniversalTagType::IA5String:
607
0
      {
608
0
        return valueRecord->castAs<Asn1IA5StringRecord>()->getValue();
609
0
      }
610
0
      case Asn1UniversalTagType::UTF8String:
611
0
      {
612
0
        return valueRecord->castAs<Asn1UTF8StringRecord>()->getValue();
613
0
      }
614
0
      default:
615
0
      {
616
0
        throw std::runtime_error("Invalid X509 certificate data: unsupported RDN value ASN.1 type: " +
617
0
                                 std::to_string(static_cast<int>(valueRecord->getUniversalTagType())));
618
0
      }
619
0
      }
620
0
    }
621
622
    std::vector<X509RelativeDistinguishedName> X509Name::getRDNs() const
623
0
    {
624
0
      std::vector<X509RelativeDistinguishedName> result;
625
0
      for (auto const& subRecord : m_Root->getSubRecords())
626
0
      {
627
0
        result.push_back(X509RelativeDistinguishedName(castRecordAs<Asn1SetRecord>(subRecord, "RDN")));
628
0
      }
629
630
0
      return result;
631
0
    }
632
633
    X509Algorithm X509AlgorithmIdentifier::getAlgorithm() const
634
0
    {
635
0
      auto oidRecord = getSubRecordAndCast<Asn1ObjectIdentifierRecord>(m_Root, algorithmOffset, "Algorithm");
636
0
      return X509Algorithm::fromOidValue(oidRecord->getValue());
637
0
    }
638
639
    X509Timestamp X509Validity::getNotBefore() const
640
0
    {
641
0
      return X509Timestamp(getSubRecordAndCast<Asn1TimeRecord>(m_Root, notBeforeOffset, "Not Before"));
642
0
    }
643
644
    X509Timestamp X509Validity::getNotAfter() const
645
0
    {
646
0
      return X509Timestamp(getSubRecordAndCast<Asn1TimeRecord>(m_Root, notAfterOffset, "Not After"));
647
0
    }
648
649
    X509AlgorithmIdentifier X509SubjectPublicKeyInfo::getAlgorithm() const
650
0
    {
651
0
      auto root =
652
0
          getSubRecordAndCast<Asn1SequenceRecord>(m_Root, algorithmOffset, "Subject Public Key Algorithm");
653
0
      return X509AlgorithmIdentifier(root);
654
0
    }
655
656
    X509Key X509SubjectPublicKeyInfo::getSubjectPublicKey() const
657
0
    {
658
0
      return X509Key(
659
0
          getSubRecordAndCast<Asn1BitStringRecord>(m_Root, subjectPublicKeyOffset, "Subject Public Key")
660
0
              ->getVecValue());
661
0
    }
662
663
0
    X509Extension::X509Extension(Asn1SequenceRecord* root) : X509Base(root)
664
0
    {
665
0
      if (root->getSubRecords().size() > 2)
666
0
      {
667
0
        m_CriticalOffset = 1;
668
0
        m_ExtensionValueOffset = 2;
669
0
      }
670
0
    }
671
672
    X509ExtensionType X509Extension::getType() const
673
0
    {
674
0
      auto extensionTypeRecord =
675
0
          getSubRecordAndCast<Asn1ObjectIdentifierRecord>(m_Root, extensionIdOffset, "Extension Type");
676
0
      return X509ExtensionType::fromOidValue(extensionTypeRecord->getValue());
677
0
    }
678
679
    bool X509Extension::isCritical() const
680
0
    {
681
0
      if (m_CriticalOffset == -1)
682
0
      {
683
0
        return false;
684
0
      }
685
686
0
      return getSubRecordAndCast<Asn1BooleanRecord>(m_Root, m_CriticalOffset, "Extension Critical")->getValue();
687
0
    }
688
689
    std::string X509Extension::getValue() const
690
0
    {
691
0
      return getSubRecordAndCast<Asn1OctetStringRecord>(m_Root, m_ExtensionValueOffset, "Extension Value")
692
0
          ->getValue();
693
0
    }
694
695
    std::vector<X509Extension> X509Extensions::getExtensions() const
696
0
    {
697
0
      std::vector<X509Extension> result;
698
0
      auto extensionsRecord = getSubRecordAndCast<Asn1SequenceRecord>(m_Root, 0, "Extensions");
699
0
      for (const auto& extension : extensionsRecord->getSubRecords())
700
0
      {
701
0
        result.push_back(X509Extension(castRecordAs<Asn1SequenceRecord>(extension, "Extension")));
702
0
      }
703
704
0
      return result;
705
0
    }
706
707
    bool X509Extensions::isValidExtensionsRecord(const Asn1Record* record)
708
0
    {
709
0
      return (record->getTagClass() == Asn1TagClass::ContextSpecific && record->getTagType() == 3 &&
710
0
              record->isConstructed());
711
0
    }
712
713
    X509SerialNumber X509TBSCertificate::getSerialNumber() const
714
0
    {
715
0
      auto serialNumber = getSubRecordAndCast<Asn1IntegerRecord>(m_Root, m_SerialNumberOffset, "Serial Number")
716
0
                              ->getValueAsString(true);
717
0
      return X509SerialNumber(serialNumber);
718
0
    }
719
720
    X509AlgorithmIdentifier X509TBSCertificate::getSignature() const
721
0
    {
722
0
      auto root = getSubRecordAndCast<Asn1SequenceRecord>(m_Root, m_SignatureOffset, "Signature Algorithm");
723
0
      return X509AlgorithmIdentifier(root);
724
0
    }
725
726
0
    X509TBSCertificate::X509TBSCertificate(Asn1SequenceRecord* root) : X509Base(root)
727
0
    {
728
0
      try
729
0
      {
730
0
        int currIndex = 0;
731
0
        auto record = root->getSubRecords().at(currIndex);
732
0
        if (X509VersionRecord::isValidVersionRecord(record))
733
0
        {
734
0
          m_VersionOffset = currIndex++;
735
0
        }
736
737
0
        m_SerialNumberOffset = currIndex++;
738
0
        m_SignatureOffset = currIndex++;
739
0
        m_IssuerOffset = currIndex++;
740
0
        m_ValidityOffset = currIndex++;
741
0
        m_SubjectOffset = currIndex++;
742
0
        m_SubjectPublicKeyInfoOffset = currIndex++;
743
744
0
        if (root->getSubRecords().size() > static_cast<size_t>(currIndex))
745
0
        {
746
0
          record = root->getSubRecords().at(currIndex);
747
748
0
          if (record->getTagClass() == Asn1TagClass::ContextSpecific && record->getTagType() == 1)
749
0
          {
750
0
            m_IssuerUniqueID = currIndex++;
751
0
            record = root->getSubRecords().at(currIndex);
752
0
          }
753
754
0
          if (record->getTagClass() == Asn1TagClass::ContextSpecific && record->getTagType() == 2)
755
0
          {
756
0
            m_SubjectUniqueID = currIndex++;
757
0
            record = root->getSubRecords().at(currIndex);
758
0
          }
759
760
0
          if (X509Extensions::isValidExtensionsRecord(record))
761
0
          {
762
0
            m_ExtensionsOffset = currIndex++;
763
0
          }
764
0
        }
765
0
      }
766
0
      catch (const std::out_of_range&)
767
0
      {
768
0
        throw std::runtime_error("Invalid X509 certificate data: TBS Certificate");
769
0
      }
770
0
    }
771
772
    X509Version X509TBSCertificate::getVersion() const
773
0
    {
774
0
      if (m_VersionOffset == -1)
775
0
      {
776
0
        return X509Version::V1;
777
0
      }
778
779
0
      auto versionAsn1Record = getSubRecordAndCast<Asn1ConstructedRecord>(m_Root, m_VersionOffset, "Version");
780
0
      auto versionRecord = X509VersionRecord(versionAsn1Record);
781
0
      return versionRecord.getVersion();
782
0
    }
783
784
    X509Name X509TBSCertificate::getIssuer() const
785
0
    {
786
0
      auto root = getSubRecordAndCast<Asn1SequenceRecord>(m_Root, m_IssuerOffset, "Issuer");
787
0
      return X509Name(root);
788
0
    }
789
790
    X509Validity X509TBSCertificate::getValidity() const
791
0
    {
792
0
      auto root = getSubRecordAndCast<Asn1SequenceRecord>(m_Root, m_ValidityOffset, "Validity");
793
0
      return X509Validity(root);
794
0
    }
795
796
    X509Name X509TBSCertificate::getSubject() const
797
0
    {
798
0
      auto root = getSubRecordAndCast<Asn1SequenceRecord>(m_Root, m_SubjectOffset, "Subject");
799
0
      return X509Name(root);
800
0
    }
801
802
    X509SubjectPublicKeyInfo X509TBSCertificate::getSubjectPublicKeyInfo() const
803
0
    {
804
0
      auto root = getSubRecordAndCast<Asn1SequenceRecord>(m_Root, m_SubjectPublicKeyInfoOffset,
805
0
                                                          "Subject Public Key Info");
806
0
      return X509SubjectPublicKeyInfo(root);
807
0
    }
808
809
    std::unique_ptr<X509Extensions> X509TBSCertificate::getExtensions() const
810
0
    {
811
0
      if (m_ExtensionsOffset == -1)
812
0
      {
813
0
        return nullptr;
814
0
      }
815
816
0
      auto root = getSubRecordAndCast<Asn1ConstructedRecord>(m_Root, m_ExtensionsOffset, "Extensions");
817
0
      return std::unique_ptr<X509Extensions>(new X509Extensions(root));
818
0
    }
819
820
    std::unique_ptr<X509Certificate> X509Certificate::decode(const uint8_t* data, size_t dataLen)
821
0
    {
822
0
      return std::unique_ptr<X509Certificate>(new X509Certificate(Asn1Record::decode(data, dataLen)));
823
0
    }
824
825
    Asn1SequenceRecord* X509Certificate::getAsn1Root() const
826
0
    {
827
0
      return castRecordAs<Asn1SequenceRecord>(m_Root.get(), "Root");
828
0
    }
829
830
    X509TBSCertificate X509Certificate::getTbsCertificate() const
831
0
    {
832
0
      auto root = getSubRecordAndCast<Asn1SequenceRecord>(getAsn1Root(), tbsCertificateOffset, "TBS Certificate");
833
0
      return X509TBSCertificate(root);
834
0
    }
835
836
    X509AlgorithmIdentifier X509Certificate::getSignatureAlgorithm() const
837
0
    {
838
0
      auto root =
839
0
          getSubRecordAndCast<Asn1SequenceRecord>(getAsn1Root(), signatureAlgorithmOffset, "Signature Algorithm");
840
0
      return X509AlgorithmIdentifier(root);
841
0
    }
842
843
    X509Key X509Certificate::getSignature() const
844
0
    {
845
0
      return X509Key(
846
0
          getSubRecordAndCast<Asn1BitStringRecord>(getAsn1Root(), signatureOffset, "Signature")->getVecValue());
847
0
    }
848
849
    std::vector<uint8_t> X509Certificate::encode()
850
0
    {
851
0
      return m_Root->encode();
852
0
    }
853
  }  // namespace X509Internal
854
855
  X509Name::X509Name(const X509Internal::X509Name& internalName)
856
0
  {
857
0
    for (const auto& rdn : internalName.getRDNs())
858
0
    {
859
0
      m_RDNs.emplace_back(RDN{ rdn.getType(), rdn.getValue() });
860
0
    }
861
0
  }
862
863
  std::string X509Name::toString(const std::string& delimiter) const
864
0
  {
865
0
    std::ostringstream result;
866
0
    bool first = true;
867
868
0
    for (const auto& rdn : m_RDNs)
869
0
    {
870
0
      if (!first)
871
0
      {
872
0
        result << delimiter;
873
0
      }
874
0
      result << rdn.type.getShortName() << "=" << rdn.value;
875
0
      first = false;
876
0
    }
877
878
0
    return result.str();
879
0
  }
880
881
  X509Extension::X509Extension(const X509Internal::X509Extension& internalExtension)
882
0
      : m_IsCritical(internalExtension.isCritical()), m_Type(internalExtension.getType()),
883
0
        m_Data(internalExtension.getValue())
884
0
  {}
885
886
  std::unique_ptr<X509ExtensionData> X509Extension::getData() const
887
0
  {
888
0
    switch (m_Type)
889
0
    {
890
0
    case X509ExtensionType::BasicConstraints:
891
0
    {
892
0
      return std::unique_ptr<X509ExtensionData>(new X509BasicConstraintsExtension(m_Data));
893
0
    }
894
0
    case X509ExtensionType::SubjectKeyIdentifier:
895
0
    {
896
0
      return std::unique_ptr<X509ExtensionData>(new X509SubjectKeyIdentifierExtension(m_Data));
897
0
    }
898
0
    case X509ExtensionType::KeyUsage:
899
0
    {
900
0
      return std::unique_ptr<X509ExtensionData>(new X509KeyUsageExtension(m_Data));
901
0
    }
902
0
    case X509ExtensionType::ExtendedKeyUsage:
903
0
    {
904
0
      return std::unique_ptr<X509ExtensionData>(new X509ExtendedKeyUsageExtension(m_Data));
905
0
    }
906
0
    default:
907
0
    {
908
0
      return {};
909
0
    }
910
0
    }
911
0
  }
912
913
  X509Certificate::X509Certificate(uint8_t* derData, size_t derDataLen, bool ownDerData)
914
0
      : m_X509Internal(X509Internal::X509Certificate::decode(derData, derDataLen)),
915
0
        m_TBSCertificate(m_X509Internal->getTbsCertificate())
916
0
  {
917
0
    if (ownDerData)
918
0
    {
919
0
      m_DerData.reset(derData);
920
0
    }
921
0
  }
922
923
  X509Certificate::X509Certificate(std::unique_ptr<uint8_t[]> derData, size_t derDataLen)
924
0
      : m_X509Internal(X509Internal::X509Certificate::decode(derData.get(), derDataLen)),
925
0
        m_TBSCertificate(m_X509Internal->getTbsCertificate())
926
0
  {
927
0
    m_DerData = std::move(derData);
928
0
  }
929
930
  X509Version X509Certificate::getVersion() const
931
0
  {
932
0
    return m_TBSCertificate.getVersion();
933
0
  }
934
935
  const std::vector<X509Extension>& X509Certificate::getExtensions() const
936
0
  {
937
0
    if (!m_ExtensionsParsed)
938
0
    {
939
0
      auto extensions = m_TBSCertificate.getExtensions();
940
0
      if (extensions != nullptr)
941
0
      {
942
0
        for (const auto& extension : extensions->getExtensions())
943
0
        {
944
0
          m_Extensions.emplace_back(X509Extension(extension));
945
0
        }
946
0
      }
947
0
      m_ExtensionsParsed = true;
948
0
    }
949
950
0
    return m_Extensions;
951
0
  }
952
953
  bool X509Certificate::hasExtension(const X509ExtensionType& extensionType) const
954
0
  {
955
0
    auto extensions = m_TBSCertificate.getExtensions()->getExtensions();
956
0
    return std::any_of(extensions.begin(), extensions.end(),
957
0
                       [extensionType](const auto& ext) { return ext.getType() == extensionType; });
958
0
  }
959
960
  const X509Extension* X509Certificate::getExtension(X509ExtensionType extensionType) const
961
0
  {
962
0
    const auto& extensions = getExtensions();
963
0
    auto matchExtension =
964
0
        std::find_if(extensions.begin(), extensions.end(), [&extensionType](const X509Extension& extension) {
965
0
          return extension.getType() == extensionType;
966
0
        });
967
968
0
    if (matchExtension != extensions.end())
969
0
    {
970
0
      return &(*matchExtension);
971
0
    }
972
973
0
    return nullptr;
974
0
  }
975
976
  X509Name X509Certificate::getSubject() const
977
0
  {
978
0
    return X509Name(m_TBSCertificate.getSubject());
979
0
  }
980
981
  X509Name X509Certificate::getIssuer() const
982
0
  {
983
0
    return X509Name(m_TBSCertificate.getIssuer());
984
0
  }
985
986
  X509SerialNumber X509Certificate::getSerialNumber() const
987
0
  {
988
0
    return m_TBSCertificate.getSerialNumber();
989
0
  }
990
991
  X509Timestamp X509Certificate::getNotBefore() const
992
0
  {
993
0
    return m_TBSCertificate.getValidity().getNotBefore();
994
0
  }
995
996
  X509Timestamp X509Certificate::getNotAfter() const
997
0
  {
998
0
    return m_TBSCertificate.getValidity().getNotAfter();
999
0
  }
1000
1001
  X509Algorithm X509Certificate::getPublicKeyAlgorithm() const
1002
0
  {
1003
0
    return m_TBSCertificate.getSubjectPublicKeyInfo().getAlgorithm().getAlgorithm();
1004
0
  }
1005
1006
  X509Key X509Certificate::getPublicKey() const
1007
0
  {
1008
0
    return m_TBSCertificate.getSubjectPublicKeyInfo().getSubjectPublicKey();
1009
0
  }
1010
1011
  X509Algorithm X509Certificate::getSignatureAlgorithm() const
1012
0
  {
1013
0
    return m_X509Internal->getSignatureAlgorithm().getAlgorithm();
1014
0
  }
1015
1016
  X509Key X509Certificate::getSignature() const
1017
0
  {
1018
0
    return m_X509Internal->getSignature();
1019
0
  }
1020
1021
  const X509Internal::X509Certificate* X509Certificate::getRawCertificate() const
1022
0
  {
1023
0
    return m_X509Internal.get();
1024
0
  }
1025
1026
  std::vector<uint8_t> X509Certificate::toDER() const
1027
0
  {
1028
0
    return m_X509Internal->encode();
1029
0
  }
1030
1031
  std::string X509Certificate::toPEM() const
1032
0
  {
1033
0
    return PemCodec::encode(m_X509Internal->encode(), pemLabel);
1034
0
  }
1035
1036
  std::string X509Certificate::toJson(int indent) const
1037
0
  {
1038
0
    auto extensions = nlohmann::ordered_json::array();
1039
0
    for (const auto& extension : getExtensions())
1040
0
    {
1041
0
      extensions.push_back({
1042
0
          { "type",       extension.getType().toString() },
1043
0
          { "isCritical", extension.isCritical()         },
1044
0
      });
1045
0
    }
1046
1047
0
    nlohmann::ordered_json certificateJson = {
1048
0
      { "version",              static_cast<int>(getVersion()) + 1 },
1049
0
      { "serialNumber",         getSerialNumber().toString()       },
1050
0
      { "issuer",               getIssuer().toString()             },
1051
0
      { "validity",
1052
0
             {
1053
0
            { "notBefore", getNotBefore().toString() },
1054
0
            { "notAfter", getNotAfter().toString() },
1055
0
        }                                                   },
1056
0
      { "subject",              getSubject().toString()            },
1057
0
      { "subjectPublicKeyInfo",
1058
0
             { { "subjectPublicKeyAlgorithm", getPublicKeyAlgorithm().toString() },
1059
0
          { "subjectPublicKey", getPublicKey().toString() } }      },
1060
0
      { "extensions",           extensions                         },
1061
0
      { "signatureAlgorithm",   getSignatureAlgorithm().toString() },
1062
0
      { "signature",            getSignature().toString()          }
1063
0
    };
1064
1065
0
    return certificateJson.dump(indent);
1066
0
  }
1067
}  // namespace pcpp