Coverage Report

Created: 2025-08-26 07:54

/src/PcapPlusPlus/Packet++/src/Asn1Codec.cpp
Line
Count
Source (jump to first uncovered line)
1
#define LOG_MODULE PacketLogModuleAsn1Codec
2
3
#include "Asn1Codec.h"
4
#include "GeneralUtils.h"
5
#include "EndianPortable.h"
6
#include "SystemUtils.h"
7
#include <unordered_map>
8
#include <numeric>
9
#include <algorithm>
10
#include <iostream>
11
#include <iomanip>
12
#include <sstream>
13
#include <cmath>
14
#include <limits>
15
#include <cstring>
16
17
#if defined(_WIN32)
18
# undef max
19
#endif
20
21
namespace pcpp
22
{
23
  const std::unordered_map<Asn1TagClass, std::string, EnumClassHash<Asn1TagClass>> Asn1TagClassToString{
24
    { Asn1TagClass::Universal,       "Universal"       },
25
    { Asn1TagClass::ContextSpecific, "ContextSpecific" },
26
    { Asn1TagClass::Application,     "Application"     },
27
    { Asn1TagClass::Private,         "Private"         }
28
  };
29
30
  std::string toString(Asn1TagClass tagClass)
31
0
  {
32
0
    if (Asn1TagClassToString.find(tagClass) != Asn1TagClassToString.end())
33
0
    {
34
0
      return Asn1TagClassToString.at(tagClass);
35
0
    }
36
37
0
    return "Unknown";
38
0
  }
39
40
  const std::unordered_map<Asn1UniversalTagType, std::string, EnumClassHash<Asn1UniversalTagType>>
41
      Asn1UniversalTagTypeToString{
42
        { Asn1UniversalTagType::EndOfContent,                "EndOfContent"                },
43
        { Asn1UniversalTagType::Boolean,                     "Boolean"                     },
44
        { Asn1UniversalTagType::Integer,                     "Integer"                     },
45
        { Asn1UniversalTagType::BitString,                   "BitString"                   },
46
        { Asn1UniversalTagType::OctetString,                 "OctetString"                 },
47
        { Asn1UniversalTagType::Null,                        "Null"                        },
48
        { Asn1UniversalTagType::ObjectIdentifier,            "ObjectIdentifier"            },
49
        { Asn1UniversalTagType::ObjectDescriptor,            "ObjectDescriptor"            },
50
        { Asn1UniversalTagType::External,                    "External"                    },
51
        { Asn1UniversalTagType::Real,                        "Real"                        },
52
        { Asn1UniversalTagType::Enumerated,                  "Enumerated"                  },
53
        { Asn1UniversalTagType::EmbeddedPDV,                 "EmbeddedPDV"                 },
54
        { Asn1UniversalTagType::UTF8String,                  "UTF8String"                  },
55
        { Asn1UniversalTagType::RelativeObjectIdentifier,    "RelativeObjectIdentifier"    },
56
        { Asn1UniversalTagType::Time,                        "Time"                        },
57
        { Asn1UniversalTagType::Reserved,                    "Reserved"                    },
58
        { Asn1UniversalTagType::Sequence,                    "Sequence"                    },
59
        { Asn1UniversalTagType::Set,                         "Set"                         },
60
        { Asn1UniversalTagType::NumericString,               "NumericString"               },
61
        { Asn1UniversalTagType::PrintableString,             "PrintableString"             },
62
        { Asn1UniversalTagType::T61String,                   "T61String"                   },
63
        { Asn1UniversalTagType::VideotexString,              "VideotexString"              },
64
        { Asn1UniversalTagType::IA5String,                   "IA5String"                   },
65
        { Asn1UniversalTagType::UTCTime,                     "UTCTime"                     },
66
        { Asn1UniversalTagType::GeneralizedTime,             "GeneralizedTime"             },
67
        { Asn1UniversalTagType::GraphicString,               "GraphicString"               },
68
        { Asn1UniversalTagType::VisibleString,               "VisibleString"               },
69
        { Asn1UniversalTagType::GeneralString,               "GeneralString"               },
70
        { Asn1UniversalTagType::UniversalString,             "UniversalString"             },
71
        { Asn1UniversalTagType::CharacterString,             "CharacterString"             },
72
        { Asn1UniversalTagType::BMPString,                   "BMPString"                   },
73
        { Asn1UniversalTagType::Date,                        "Date"                        },
74
        { Asn1UniversalTagType::TimeOfDay,                   "TimeOfDay"                   },
75
        { Asn1UniversalTagType::DateTime,                    "DateTime"                    },
76
        { Asn1UniversalTagType::Duration,                    "Duration"                    },
77
        { Asn1UniversalTagType::ObjectIdentifierIRI,         "ObjectIdentifierIRI"         },
78
        { Asn1UniversalTagType::RelativeObjectIdentifierIRI, "RelativeObjectIdentifierIRI" },
79
        { Asn1UniversalTagType::NotApplicable,               "Unknown"                     }
80
    };
81
82
  std::string toString(Asn1UniversalTagType tagType)
83
0
  {
84
0
    if (Asn1UniversalTagTypeToString.find(tagType) != Asn1UniversalTagTypeToString.end())
85
0
    {
86
0
      return Asn1UniversalTagTypeToString.at(tagType);
87
0
    }
88
89
0
    return "Unknown";
90
0
  }
91
92
  std::unique_ptr<Asn1Record> Asn1Record::decode(const uint8_t* data, size_t dataLen, bool lazy)
93
342k
  {
94
342k
    uint8_t tagLen;
95
342k
    auto decodedRecord = decodeTagAndCreateRecord(data, dataLen, tagLen);
96
97
342k
    uint8_t lengthLen;
98
342k
    lengthLen = decodedRecord->decodeLength(data + tagLen, dataLen - tagLen);
99
100
342k
    decodedRecord->m_TotalLength = tagLen + lengthLen + decodedRecord->m_ValueLength;
101
342k
    if (decodedRecord->m_TotalLength < decodedRecord->m_ValueLength ||  // check for overflow
102
342k
        decodedRecord->m_TotalLength > dataLen)
103
1.57k
    {
104
1.57k
      throw std::invalid_argument("Cannot decode ASN.1 record, data doesn't contain the entire record");
105
1.57k
    }
106
107
341k
    uint8_t const* startOfData = data + tagLen + lengthLen;
108
341k
    internal::Asn1LoadPolicy policy = lazy ? internal::Asn1LoadPolicy::Lazy : internal::Asn1LoadPolicy::Eager;
109
341k
    decodedRecord->setEncodedValue(startOfData, policy);
110
111
341k
    return decodedRecord;
112
342k
  }
113
114
  uint8_t Asn1Record::encodeTag() const
115
0
  {
116
0
    uint8_t tagByte;
117
118
0
    switch (m_TagClass)
119
0
    {
120
0
    case Asn1TagClass::Private:
121
0
    {
122
0
      tagByte = 0xc0;
123
0
      break;
124
0
    }
125
0
    case Asn1TagClass::ContextSpecific:
126
0
    {
127
0
      tagByte = 0x80;
128
0
      break;
129
0
    }
130
0
    case Asn1TagClass::Application:
131
0
    {
132
0
      tagByte = 0x40;
133
0
      break;
134
0
    }
135
0
    default:
136
0
    {
137
0
      tagByte = 0;
138
0
      break;
139
0
    }
140
0
    }
141
142
0
    if (m_IsConstructed)
143
0
    {
144
0
      tagByte |= 0x20;
145
0
    }
146
147
0
    auto tagType = m_TagType & 0x1f;
148
0
    tagByte |= tagType;
149
150
0
    return tagByte;
151
0
  }
152
153
  std::vector<uint8_t> Asn1Record::encodeLength() const
154
0
  {
155
0
    std::vector<uint8_t> result;
156
157
0
    if (m_ValueLength < 128)
158
0
    {
159
0
      result.push_back(static_cast<uint8_t>(m_ValueLength));
160
0
      return result;
161
0
    }
162
163
0
    auto tempValueLength = m_ValueLength;
164
0
    do
165
0
    {
166
0
      uint8_t byte = tempValueLength & 0xff;
167
0
      result.push_back(byte);  // Inserts the bytes in reverse order
168
0
      tempValueLength >>= 8;
169
0
    } while (tempValueLength != 0);
170
171
0
    uint8_t firstByte = 0x80 | static_cast<uint8_t>(result.size());
172
0
    result.push_back(firstByte);
173
174
    // Reverses the bytes to get forward ordering
175
0
    std::reverse(result.begin(), result.end());
176
177
0
    return result;
178
0
  }
179
180
  std::vector<uint8_t> Asn1Record::encode() const
181
0
  {
182
0
    std::vector<uint8_t> result;
183
184
0
    result.push_back(encodeTag());
185
186
0
    auto lengthBytes = encodeLength();
187
0
    result.insert(result.end(), lengthBytes.begin(), lengthBytes.end());
188
189
0
    auto encodedValue = encodeValueSafe();
190
0
    result.insert(result.end(), encodedValue.begin(), encodedValue.end());
191
192
0
    return result;
193
0
  }
194
195
  Asn1UniversalTagType Asn1Record::getUniversalTagType() const
196
0
  {
197
0
    if (m_TagClass == Asn1TagClass::Universal)
198
0
    {
199
0
      return static_cast<Asn1UniversalTagType>(m_TagType);
200
0
    }
201
202
0
    return Asn1UniversalTagType::NotApplicable;
203
0
  }
204
205
  std::unique_ptr<Asn1Record> Asn1Record::decodeTagAndCreateRecord(const uint8_t* data, size_t dataLen,
206
                                                                   uint8_t& tagLen)
207
342k
  {
208
342k
    if (dataLen < 1)
209
0
    {
210
0
      throw std::invalid_argument("Cannot decode ASN.1 record tag");
211
0
    }
212
213
342k
    tagLen = 1;
214
215
342k
    Asn1TagClass tagClass = Asn1TagClass::Universal;
216
217
    // Check first 2 bits
218
342k
    auto tagClassBits = data[0] & 0xc0;
219
342k
    if (tagClassBits == 0)
220
270k
    {
221
270k
      tagClass = Asn1TagClass::Universal;
222
270k
    }
223
72.8k
    else if ((tagClassBits & 0xc0) == 0xc0)
224
784
    {
225
784
      tagClass = Asn1TagClass::Private;
226
784
    }
227
72.0k
    else if ((tagClassBits & 0x80) == 0x80)
228
24.1k
    {
229
24.1k
      tagClass = Asn1TagClass::ContextSpecific;
230
24.1k
    }
231
47.8k
    else if ((tagClassBits & 0x40) == 0x40)
232
47.8k
    {
233
47.8k
      tagClass = Asn1TagClass::Application;
234
47.8k
    }
235
236
    // Check bit 6
237
342k
    auto tagTypeBits = data[0] & 0x20;
238
342k
    bool isConstructed = (tagTypeBits != 0);
239
240
    // Check last 5 bits
241
342k
    auto tagType = data[0] & 0x1f;
242
342k
    if (tagType == 0x1f)
243
225
    {
244
225
      if (dataLen < 2)
245
5
      {
246
5
        throw std::invalid_argument("Cannot decode ASN.1 record tag");
247
5
      }
248
249
220
      if ((data[1] & 0x80) != 0)
250
140
      {
251
140
        throw std::invalid_argument("ASN.1 tags with value larger than 127 are not supported");
252
140
      }
253
254
80
      tagType = data[1] & 0x7f;
255
80
      tagLen = 2;
256
80
    }
257
258
342k
    std::unique_ptr<Asn1Record> newRecord;
259
260
342k
    if (isConstructed)
261
137k
    {
262
137k
      if (tagClass == Asn1TagClass::Universal)
263
78.4k
      {
264
78.4k
        switch (static_cast<Asn1UniversalTagType>(tagType))
265
78.4k
        {
266
69.3k
        case Asn1UniversalTagType::Sequence:
267
69.3k
        {
268
69.3k
          newRecord.reset(new Asn1SequenceRecord());
269
69.3k
          break;
270
0
        }
271
8.53k
        case Asn1UniversalTagType::Set:
272
8.53k
        {
273
8.53k
          newRecord.reset(new Asn1SetRecord());
274
8.53k
          break;
275
0
        }
276
591
        default:
277
591
        {
278
591
          newRecord.reset(new Asn1ConstructedRecord());
279
591
        }
280
78.4k
        }
281
78.4k
      }
282
59.0k
      else
283
59.0k
      {
284
59.0k
        newRecord.reset(new Asn1ConstructedRecord());
285
59.0k
      }
286
137k
    }
287
205k
    else
288
205k
    {
289
205k
      if (tagClass == Asn1TagClass::Universal)
290
191k
      {
291
191k
        auto asn1UniversalTagType = static_cast<Asn1UniversalTagType>(tagType);
292
191k
        switch (asn1UniversalTagType)
293
191k
        {
294
56.8k
        case Asn1UniversalTagType::Integer:
295
56.8k
        {
296
56.8k
          newRecord.reset(new Asn1IntegerRecord());
297
56.8k
          break;
298
0
        }
299
19.3k
        case Asn1UniversalTagType::Enumerated:
300
19.3k
        {
301
19.3k
          newRecord.reset(new Asn1EnumeratedRecord());
302
19.3k
          break;
303
0
        }
304
109k
        case Asn1UniversalTagType::OctetString:
305
109k
        {
306
109k
          newRecord.reset(new Asn1OctetStringRecord());
307
109k
          break;
308
0
        }
309
118
        case Asn1UniversalTagType::UTF8String:
310
118
        {
311
118
          newRecord.reset(new Asn1UTF8StringRecord());
312
118
          break;
313
0
        }
314
15
        case Asn1UniversalTagType::PrintableString:
315
15
        {
316
15
          newRecord.reset(new Asn1PrintableStringRecord());
317
15
          break;
318
0
        }
319
24
        case Asn1UniversalTagType::IA5String:
320
24
        {
321
24
          newRecord.reset(new Asn1IA5StringRecord());
322
24
          break;
323
0
        }
324
3.47k
        case Asn1UniversalTagType::Boolean:
325
3.47k
        {
326
3.47k
          newRecord.reset(new Asn1BooleanRecord());
327
3.47k
          break;
328
0
        }
329
192
        case Asn1UniversalTagType::BitString:
330
192
        {
331
192
          newRecord.reset(new Asn1BitStringRecord());
332
192
          break;
333
0
        }
334
120
        case Asn1UniversalTagType::Null:
335
120
        {
336
120
          newRecord.reset(new Asn1NullRecord());
337
120
          break;
338
0
        }
339
416
        case Asn1UniversalTagType::ObjectIdentifier:
340
416
        {
341
416
          newRecord.reset(new Asn1ObjectIdentifierRecord());
342
416
          break;
343
0
        }
344
20
        case Asn1UniversalTagType::UTCTime:
345
20
        {
346
20
          newRecord.reset(new Asn1UtcTimeRecord());
347
20
          break;
348
0
        }
349
115
        case Asn1UniversalTagType::GeneralizedTime:
350
115
        {
351
115
          newRecord.reset(new Asn1GeneralizedTimeRecord());
352
115
          break;
353
0
        }
354
1.07k
        default:
355
1.07k
        {
356
1.07k
          newRecord.reset(new Asn1GenericRecord());
357
1.07k
        }
358
191k
        }
359
191k
      }
360
13.6k
      else
361
13.6k
      {
362
13.6k
        newRecord.reset(new Asn1GenericRecord());
363
13.6k
      }
364
205k
    }
365
366
342k
    newRecord->m_TagClass = tagClass;
367
342k
    newRecord->m_IsConstructed = isConstructed;
368
342k
    newRecord->m_TagType = tagType;
369
370
342k
    return newRecord;
371
342k
  }
372
373
  uint8_t Asn1Record::decodeLength(const uint8_t* data, size_t dataLen)
374
342k
  {
375
342k
    if (dataLen < 1)
376
132
    {
377
132
      throw std::invalid_argument("Cannot decode ASN.1 record length");
378
132
    }
379
380
    // Check 8th bit
381
342k
    auto lengthForm = data[0] & 0x80;
382
383
    // Check if the tag is using more than one byte
384
    // 8th bit at 0 means the length only uses one byte
385
    // 8th bit at 1 means the length uses more than one byte. The number of bytes is encoded in the other 7 bits
386
342k
    if (lengthForm == 0)
387
252k
    {
388
252k
      m_ValueLength = data[0];
389
252k
      return 1;
390
252k
    }
391
392
90.1k
    uint8_t actualLengthBytes = data[0] & 0x7F;
393
90.1k
    const uint8_t* actualLengthData = data + 1;
394
395
90.1k
    if (dataLen < static_cast<size_t>(actualLengthBytes) + 1)
396
257
    {
397
257
      throw std::invalid_argument("Cannot decode ASN.1 record length");
398
257
    }
399
400
421k
    for (int i = 0; i < actualLengthBytes; i++)
401
331k
    {
402
331k
      size_t partialValueLength = m_ValueLength << 8;
403
331k
      if (partialValueLength < m_ValueLength)  // check for overflow
404
718
      {
405
718
        throw std::invalid_argument("Cannot decode ASN.1 record length");
406
718
      }
407
408
331k
      m_ValueLength = partialValueLength | actualLengthData[i];
409
331k
    }
410
411
89.1k
    return 1 + actualLengthBytes;
412
89.9k
  }
413
414
  void Asn1Record::decodeValueIfNeeded() const
415
375k
  {
416
    // TODO: This is not thread-safe and can cause issues in a multiple reader scenario.
417
375k
    if (m_EncodedValue != nullptr)
418
338k
    {
419
338k
      decodeValue(m_EncodedValue);
420
338k
      m_EncodedValue = nullptr;  // Clear the encoded value after decoding
421
338k
    }
422
375k
  }
423
424
  std::string Asn1Record::toString() const
425
0
  {
426
    // Decode the value if it hasn't been decoded yet to ensure we have the correct value
427
0
    decodeValueIfNeeded();
428
0
    auto lines = toStringList();
429
430
0
    auto commaSeparated = [](std::string str1, std::string str2) {
431
0
      return std::move(str1) + '\n' + std::move(str2);
432
0
    };
433
434
0
    return std::accumulate(std::next(lines.begin()), lines.end(), lines[0], commaSeparated);
435
0
  }
436
437
  std::vector<std::string> Asn1Record::toStringList() const
438
0
  {
439
0
    std::ostringstream stream;
440
441
0
    auto universalType = getUniversalTagType();
442
0
    if (universalType == Asn1UniversalTagType::NotApplicable)
443
0
    {
444
0
      stream << pcpp::toString(m_TagClass) << " (" << static_cast<int>(m_TagType) << ")";
445
0
    }
446
0
    else
447
0
    {
448
0
      stream << pcpp::toString(universalType);
449
0
    }
450
451
0
    if (m_IsConstructed)
452
0
    {
453
0
      stream << " (constructed)";
454
0
    }
455
456
0
    stream << ", Length: " << m_TotalLength - m_ValueLength << "+" << m_ValueLength;
457
458
0
    return { stream.str() };
459
0
  }
460
461
  void Asn1Record::setEncodedValue(uint8_t const* dataSource, internal::Asn1LoadPolicy loadPolicy)
462
339k
  {
463
339k
    m_EncodedValue = dataSource;
464
465
339k
    if (loadPolicy == internal::Asn1LoadPolicy::Eager)
466
290k
    {
467
290k
      decodeValueIfNeeded();
468
290k
    }
469
339k
  }
470
471
  Asn1GenericRecord::Asn1GenericRecord(Asn1TagClass tagClass, bool isConstructed, uint8_t tagType,
472
                                       const uint8_t* value, size_t valueLen)
473
0
  {
474
0
    init(tagClass, isConstructed, tagType, value, valueLen);
475
0
  }
476
477
  Asn1GenericRecord::Asn1GenericRecord(Asn1TagClass tagClass, bool isConstructed, uint8_t tagType,
478
                                       const std::string& value)
479
0
  {
480
0
    init(tagClass, isConstructed, tagType, reinterpret_cast<const uint8_t*>(value.c_str()), value.size());
481
0
  }
482
483
  void Asn1GenericRecord::decodeValue(uint8_t const* data) const
484
14.0k
  {
485
14.0k
    m_Value = std::make_unique<uint8_t[]>(m_ValueLength);
486
14.0k
    std::memcpy(m_Value.get(), data, m_ValueLength);
487
14.0k
  }
488
489
  std::vector<uint8_t> Asn1GenericRecord::encodeValue() const
490
0
  {
491
0
    return { m_Value.get(), m_Value.get() + m_ValueLength };
492
0
  }
493
494
  void Asn1GenericRecord::init(Asn1TagClass tagClass, bool isConstructed, uint8_t tagType, const uint8_t* value,
495
                               size_t valueLen)
496
0
  {
497
0
    m_TagType = tagType;
498
0
    m_TagClass = tagClass;
499
0
    m_IsConstructed = isConstructed;
500
0
    m_Value = std::make_unique<uint8_t[]>(valueLen);
501
0
    std::memcpy(m_Value.get(), value, valueLen);
502
0
    m_ValueLength = valueLen;
503
0
    m_TotalLength = m_ValueLength + 2;
504
0
  }
505
506
  Asn1ConstructedRecord::Asn1ConstructedRecord(Asn1TagClass tagClass, uint8_t tagType,
507
                                               const std::vector<Asn1Record*>& subRecords)
508
0
  {
509
0
    init(tagClass, tagType, subRecords.begin(), subRecords.end());
510
0
  }
511
512
  Asn1ConstructedRecord::Asn1ConstructedRecord(Asn1TagClass tagClass, uint8_t tagType,
513
                                               const PointerVector<Asn1Record>& subRecords)
514
0
  {
515
0
    init(tagClass, tagType, subRecords.begin(), subRecords.end());
516
0
  }
517
518
  void Asn1ConstructedRecord::decodeValue(uint8_t const* data) const
519
135k
  {
520
135k
    if (!(data || m_ValueLength))
521
0
    {
522
0
      return;
523
0
    }
524
525
135k
    auto value = data;
526
135k
    auto valueLen = m_ValueLength;
527
528
427k
    while (valueLen > 0)
529
292k
    {
530
292k
      auto subRecord = Asn1Record::decode(value, valueLen, LazySubRecordDecoding);
531
292k
      value += subRecord->getTotalLength();
532
292k
      valueLen -= subRecord->getTotalLength();
533
534
292k
      m_SubRecords.pushBack(std::move(subRecord));
535
292k
    }
536
135k
  }
537
538
  std::vector<uint8_t> Asn1ConstructedRecord::encodeValue() const
539
0
  {
540
0
    std::vector<uint8_t> result;
541
0
    result.reserve(m_ValueLength);
542
543
0
    for (auto record : m_SubRecords)
544
0
    {
545
0
      auto encodedRecord = record->encode();
546
0
      result.insert(result.end(), std::make_move_iterator(encodedRecord.begin()),
547
0
                    std::make_move_iterator(encodedRecord.end()));
548
0
    }
549
0
    return result;
550
0
  }
551
552
  std::vector<std::string> Asn1ConstructedRecord::toStringList() const
553
0
  {
554
0
    std::vector<std::string> result = { Asn1Record::toStringList().front() };
555
0
    for (auto subRecord : m_SubRecords)
556
0
    {
557
0
      for (const auto& line : subRecord->toStringList())
558
0
      {
559
0
        result.push_back("  " + line);
560
0
      }
561
0
    }
562
0
    return result;
563
0
  }
564
565
  Asn1SequenceRecord::Asn1SequenceRecord(const std::vector<Asn1Record*>& subRecords)
566
0
      : Asn1ConstructedRecord(Asn1TagClass::Universal, static_cast<uint8_t>(Asn1UniversalTagType::Sequence),
567
0
                              subRecords)
568
0
  {}
569
570
  Asn1SequenceRecord::Asn1SequenceRecord(const PointerVector<Asn1Record>& subRecords)
571
0
      : Asn1ConstructedRecord(Asn1TagClass::Universal, static_cast<uint8_t>(Asn1UniversalTagType::Sequence),
572
0
                              subRecords)
573
0
  {}
574
575
  Asn1SetRecord::Asn1SetRecord(const std::vector<Asn1Record*>& subRecords)
576
0
      : Asn1ConstructedRecord(Asn1TagClass::Universal, static_cast<uint8_t>(Asn1UniversalTagType::Set), subRecords)
577
0
  {}
578
579
  Asn1SetRecord::Asn1SetRecord(const PointerVector<Asn1Record>& subRecords)
580
0
      : Asn1ConstructedRecord(Asn1TagClass::Universal, static_cast<uint8_t>(Asn1UniversalTagType::Set), subRecords)
581
0
  {}
582
583
109k
  Asn1PrimitiveRecord::Asn1PrimitiveRecord(Asn1UniversalTagType tagType) : Asn1Record()
584
109k
  {
585
109k
    m_TagType = static_cast<uint8_t>(tagType);
586
109k
    m_TagClass = Asn1TagClass::Universal;
587
109k
    m_IsConstructed = false;
588
109k
  }
589
590
  Asn1IntegerRecord::BigInt::BigInt(const std::string& value)
591
0
  {
592
0
    m_Value = initFromString(value);
593
0
  }
594
595
  Asn1IntegerRecord::BigInt::BigInt(const BigInt& other)
596
0
  {
597
0
    m_Value = other.m_Value;
598
0
  }
599
600
  std::string Asn1IntegerRecord::BigInt::initFromString(const std::string& value)
601
75.7k
  {
602
75.7k
    std::string valueStr = value;
603
604
    // Optional 0x or 0X prefix
605
75.7k
    if (value.size() >= 2 && value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
606
0
    {
607
0
      valueStr = value.substr(2);
608
0
    }
609
610
75.7k
    if (valueStr.empty())
611
5
    {
612
5
      throw std::invalid_argument("Value is not a valid hex stream");
613
5
    }
614
615
168k
    if (std::any_of(valueStr.begin(), valueStr.end(), [](char c) { return !std::isxdigit(c); }))
616
0
    {
617
0
      throw std::invalid_argument("Value is not a valid hex stream");
618
0
    }
619
620
75.7k
    return valueStr;
621
75.7k
  }
622
623
  Asn1IntegerRecord::BigInt& Asn1IntegerRecord::BigInt::operator=(const std::string& value)
624
75.7k
  {
625
75.7k
    m_Value = initFromString(value);
626
75.7k
    return *this;
627
75.7k
  }
628
629
  size_t Asn1IntegerRecord::BigInt::size() const
630
0
  {
631
0
    return m_Value.size() / 2;
632
0
  }
633
634
  std::string Asn1IntegerRecord::BigInt::toString() const
635
0
  {
636
0
    return m_Value;
637
0
  }
638
639
  std::vector<uint8_t> Asn1IntegerRecord::BigInt::toBytes() const
640
0
  {
641
0
    std::string value = m_Value;
642
0
    if (m_Value.size() % 2 != 0)
643
0
    {
644
0
      value.insert(0, 1, '0');
645
0
    }
646
647
0
    std::vector<uint8_t> result;
648
0
    for (std::size_t i = 0; i < value.size(); i += 2)
649
0
    {
650
0
      std::string byteStr = value.substr(i, 2);
651
0
      auto byte = static_cast<uint8_t>(std::stoul(byteStr, nullptr, 16));
652
0
      result.push_back(byte);
653
0
    }
654
655
0
    return result;
656
0
  }
657
658
0
  Asn1IntegerRecord::Asn1IntegerRecord(uint64_t value) : Asn1PrimitiveRecord(Asn1UniversalTagType::Integer)
659
0
  {
660
0
    m_Value = value;
661
662
0
    std::size_t length = 0;
663
0
    while (value != 0)
664
0
    {
665
0
      ++length;
666
0
      value >>= 8;
667
0
    }
668
0
    m_ValueLength = length == 0 ? 1 : length;
669
670
0
    m_TotalLength = m_ValueLength + 2;
671
0
  }
672
673
0
  Asn1IntegerRecord::Asn1IntegerRecord(const std::string& value) : Asn1PrimitiveRecord(Asn1UniversalTagType::Integer)
674
0
  {
675
0
    m_Value = value;
676
0
    m_ValueLength = m_Value.size();
677
0
    m_TotalLength = m_ValueLength + 2;
678
0
  }
679
680
  void Asn1IntegerRecord::decodeValue(uint8_t const* data) const
681
75.7k
  {
682
75.7k
    m_Value = pcpp::byteArrayToHexString(data, m_ValueLength);
683
75.7k
  }
684
685
  std::vector<uint8_t> Asn1IntegerRecord::encodeValue() const
686
0
  {
687
0
    return m_Value.toBytes();
688
0
  }
689
690
  std::vector<std::string> Asn1IntegerRecord::toStringList() const
691
0
  {
692
0
    auto valueAsString =
693
0
        m_Value.canFit<uint64_t>() ? std::to_string(getIntValue<uint64_t>()) : "0x" + getValueAsString();
694
0
    return std::vector<std::string>({ Asn1Record::toStringList().front() + ", Value: " + valueAsString });
695
0
  }
696
697
0
  Asn1EnumeratedRecord::Asn1EnumeratedRecord(uint32_t value) : Asn1IntegerRecord(value)
698
0
  {
699
0
    m_TagType = static_cast<uint8_t>(Asn1UniversalTagType::Enumerated);
700
0
  }
701
702
0
  Asn1OctetStringRecord::Asn1OctetStringRecord(const uint8_t* value, size_t valueLength) : m_IsPrintable(false)
703
0
  {
704
0
    m_Value = byteArrayToHexString(value, valueLength);
705
0
    m_ValueLength = valueLength;
706
0
    m_TotalLength = m_ValueLength + 2;
707
0
  }
708
709
  void Asn1OctetStringRecord::decodeValue(uint8_t const* data) const
710
109k
  {
711
109k
    auto value = reinterpret_cast<char const*>(data);
712
713
2.47M
    m_IsPrintable = std::all_of(value, value + m_ValueLength, [](char c) { return isprint(0xff & c); });
714
715
109k
    if (m_IsPrintable)
716
97.5k
    {
717
97.5k
      Asn1StringRecord::decodeValue(data);
718
97.5k
    }
719
11.7k
    else
720
11.7k
    {
721
11.7k
      m_Value = byteArrayToHexString(data, m_ValueLength);
722
11.7k
    }
723
109k
  }
724
725
  std::vector<uint8_t> Asn1OctetStringRecord::encodeValue() const
726
0
  {
727
0
    if (m_IsPrintable)
728
0
    {
729
0
      return Asn1StringRecord::encodeValue();
730
0
    }
731
732
    // converting the hex stream to a byte array.
733
    // The byte array size is half the size of the string
734
    // i.e "1a2b" (length == 4)  becomes {0x1a, 0x2b} (length == 2)
735
0
    auto rawValueSize = static_cast<size_t>(m_Value.size() / 2);
736
0
    std::vector<uint8_t> rawValue;
737
0
    rawValue.resize(rawValueSize);
738
0
    hexStringToByteArray(m_Value, rawValue.data(), rawValueSize);
739
0
    return rawValue;
740
0
  }
741
742
0
  Asn1BooleanRecord::Asn1BooleanRecord(bool value) : Asn1PrimitiveRecord(Asn1UniversalTagType::Boolean)
743
0
  {
744
0
    m_Value = value;
745
0
    m_ValueLength = 1;
746
0
    m_TotalLength = 3;
747
0
  }
748
749
  void Asn1BooleanRecord::decodeValue(uint8_t const* data) const
750
3.44k
  {
751
3.44k
    m_Value = data[0] != 0;
752
3.44k
  }
753
754
  std::vector<uint8_t> Asn1BooleanRecord::encodeValue() const
755
0
  {
756
0
    uint8_t byte = (m_Value ? 0xff : 0x00);
757
0
    return { byte };
758
0
  }
759
760
  std::vector<std::string> Asn1BooleanRecord::toStringList() const
761
0
  {
762
0
    return { Asn1Record::toStringList().front() + ", Value: " + (getValue() ? "true" : "false") };
763
0
  }
764
765
120
  Asn1NullRecord::Asn1NullRecord() : Asn1PrimitiveRecord(Asn1UniversalTagType::Null)
766
120
  {
767
120
    m_ValueLength = 0;
768
120
    m_TotalLength = 2;
769
120
  }
770
771
  Asn1ObjectIdentifier::Asn1ObjectIdentifier(const uint8_t* data, size_t dataLen)
772
371
  {
773
    // A description of OID encoding can be found here:
774
    // https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-object-identifier?redirectedfrom=MSDN
775
776
371
    if (!data || dataLen == 0)
777
85
    {
778
85
      throw std::invalid_argument("Malformed OID: Not enough bytes for the first component");
779
85
    }
780
781
286
    size_t currentByteIndex = 0;
782
286
    std::vector<uint32_t> components;
783
784
286
    uint8_t firstByte = data[currentByteIndex++];
785
    // Decode the first byte: first_component * 40 + second_component
786
286
    components.push_back(static_cast<uint32_t>(firstByte / 40));
787
286
    components.push_back(static_cast<uint32_t>(firstByte % 40));
788
789
286
    uint32_t currentComponentValue = 0;
790
286
    bool componentStarted = false;
791
792
    // Process remaining bytes using base-128 encoding
793
4.96k
    while (currentByteIndex < dataLen)
794
4.68k
    {
795
4.68k
      uint8_t byte = data[currentByteIndex++];
796
797
      // Shift previous bits left by 7 and append lower 7 bits
798
4.68k
      currentComponentValue = (currentComponentValue << 7) | (byte & 0x7f);
799
4.68k
      componentStarted = true;
800
801
      // If the MSB is 0, this is the final byte of the current value
802
4.68k
      if ((byte & 0x80) == 0)
803
4.06k
      {
804
4.06k
        components.push_back(currentComponentValue);
805
4.06k
        currentComponentValue = 0;
806
4.06k
        componentStarted = false;
807
4.06k
      }
808
4.68k
    }
809
810
286
    if (componentStarted)
811
15
    {
812
15
      throw std::invalid_argument("Malformed OID: Incomplete component at end of data");
813
15
    }
814
815
271
    m_Components = components;
816
271
  }
817
818
  Asn1ObjectIdentifier::Asn1ObjectIdentifier(const std::string& oidString)
819
0
  {
820
0
    std::vector<uint32_t> components;
821
0
    std::istringstream stream(oidString);
822
0
    std::string token;
823
824
0
    while (std::getline(stream, token, '.'))
825
0
    {
826
0
      if (token.empty())
827
0
      {
828
0
        throw std::invalid_argument("Malformed OID: empty component");
829
0
      }
830
831
0
      unsigned long long value;
832
0
      try
833
0
      {
834
0
        value = std::stoull(token);
835
0
      }
836
0
      catch (const std::exception&)
837
0
      {
838
0
        throw std::invalid_argument("Malformed OID: invalid component");
839
0
      }
840
841
0
      if (value > std::numeric_limits<uint32_t>::max())
842
0
      {
843
0
        throw std::invalid_argument("Malformed OID: component out of uint32_t range");
844
0
      }
845
846
0
      components.push_back(static_cast<uint32_t>(value));
847
0
    }
848
849
0
    if (components.size() < 2)
850
0
    {
851
0
      throw std::invalid_argument("Malformed OID: an OID must have at least two components");
852
0
    }
853
854
0
    if (components[0] > 2)
855
0
    {
856
0
      throw std::invalid_argument("Malformed OID: first component must be 0, 1, or 2");
857
0
    }
858
859
0
    if ((components[0] == 0 || components[0] == 1) && components[1] >= 40)
860
0
    {
861
0
      throw std::invalid_argument(
862
0
          "Malformed OID: second component must be less than 40 when first component is 0 or 1");
863
0
    }
864
865
0
    m_Components = components;
866
0
  }
867
868
  std::string Asn1ObjectIdentifier::toString() const
869
0
  {
870
0
    if (m_Components.empty())
871
0
    {
872
0
      return "";
873
0
    }
874
875
0
    std::ostringstream stream;
876
0
    stream << m_Components[0];
877
878
0
    for (size_t i = 1; i < m_Components.size(); ++i)
879
0
    {
880
0
      stream << "." << m_Components[i];
881
0
    }
882
0
    return stream.str();
883
0
  }
884
885
  std::vector<uint8_t> Asn1ObjectIdentifier::toBytes() const
886
0
  {
887
    // A description of OID encoding can be found here:
888
    // https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-object-identifier?redirectedfrom=MSDN
889
890
0
    if (m_Components.size() < 2)
891
0
    {
892
0
      throw std::runtime_error("OID must have at least two components to encode.");
893
0
    }
894
895
0
    std::vector<uint8_t> encoded;
896
897
    // Encode the first two components into one byte
898
0
    uint32_t firstComponent = m_Components[0];
899
0
    uint32_t secondComponent = m_Components[1];
900
0
    encoded.push_back(static_cast<uint8_t>(firstComponent * 40 + secondComponent));
901
902
    // Encode remaining components using base-128 encoding
903
0
    for (size_t i = 2; i < m_Components.size(); ++i)
904
0
    {
905
0
      uint32_t currentComponent = m_Components[i];
906
0
      std::vector<uint8_t> temp;
907
908
      // At least one byte must be generated even if value is 0
909
0
      do
910
0
      {
911
0
        temp.push_back(static_cast<uint8_t>(currentComponent & 0x7F));
912
0
        currentComponent >>= 7;
913
0
      } while (currentComponent > 0);
914
915
      // Set continuation bits (MSB) for all but the last byte
916
0
      for (size_t j = temp.size(); j-- > 0;)
917
0
      {
918
0
        uint8_t byte = temp[j];
919
0
        if (j != 0)
920
0
        {
921
0
          byte |= 0x80;
922
0
        }
923
0
        encoded.push_back(byte);
924
0
      }
925
0
    }
926
927
0
    return encoded;
928
0
  }
929
930
  Asn1ObjectIdentifierRecord::Asn1ObjectIdentifierRecord(const Asn1ObjectIdentifier& value)
931
0
      : Asn1PrimitiveRecord(Asn1UniversalTagType::ObjectIdentifier)
932
0
  {
933
0
    m_Value = value;
934
0
    m_ValueLength = value.toBytes().size();
935
0
    m_TotalLength = m_ValueLength + 2;
936
0
  }
937
938
  void Asn1ObjectIdentifierRecord::decodeValue(uint8_t const* data) const
939
371
  {
940
371
    m_Value = Asn1ObjectIdentifier(data, m_ValueLength);
941
371
  }
942
943
  std::vector<uint8_t> Asn1ObjectIdentifierRecord::encodeValue() const
944
0
  {
945
0
    return m_Value.toBytes();
946
0
  }
947
948
  std::vector<std::string> Asn1ObjectIdentifierRecord::toStringList() const
949
0
  {
950
0
    return { Asn1Record::toStringList().front() + ", Value: " + getValue().toString() };
951
0
  }
952
953
  Asn1TimeRecord::Asn1TimeRecord(Asn1UniversalTagType tagType, const std::chrono::system_clock::time_point& value,
954
                                 const std::string& timezone)
955
0
      : Asn1PrimitiveRecord(tagType)
956
0
  {
957
0
    validateTimezone(timezone);
958
0
    m_Value = adjustTimezones(value, timezone, "Z");
959
0
  }
960
961
  std::string Asn1TimeRecord::getValueAsString(const std::string& format, const std::string& timezone,
962
                                               bool includeMilliseconds) const
963
0
  {
964
0
    auto value = getValue(timezone);
965
0
    auto timeValue = std::chrono::system_clock::to_time_t(value);
966
0
    auto tmValue = *std::gmtime(&timeValue);
967
968
0
    std::ostringstream osstream;
969
0
    osstream << std::put_time(&tmValue, format.c_str());
970
971
0
    if (includeMilliseconds)
972
0
    {
973
0
      auto milliseconds =
974
0
          std::chrono::duration_cast<std::chrono::milliseconds>(value.time_since_epoch()).count() % 1000;
975
0
      if (milliseconds != 0)
976
0
      {
977
0
        osstream << "." << std::setw(3) << std::setfill('0') << milliseconds;
978
0
      }
979
0
    }
980
981
0
    if (timezone != "Z")
982
0
    {
983
0
      osstream << " UTC" << timezone;
984
0
    }
985
986
0
    return osstream.str();
987
0
  }
988
989
  std::vector<std::string> Asn1TimeRecord::toStringList() const
990
0
  {
991
0
    return { Asn1Record::toStringList().front() + ", Value: " + getValueAsString("%Y-%m-%d %H:%M:%S", "Z", true) };
992
0
  }
993
994
  void Asn1TimeRecord::validateTimezone(const std::string& timezone)
995
0
  {
996
0
    if (timezone == "Z")
997
0
    {
998
0
      return;
999
0
    }
1000
1001
0
    if (timezone.length() != 5 || (timezone[0] != '+' && timezone[0] != '-') || !std::isdigit(timezone[1]) ||
1002
0
        !std::isdigit(timezone[2]) || !std::isdigit(timezone[3]) || !std::isdigit(timezone[4]))
1003
0
    {
1004
0
      throw std::invalid_argument("Invalid timezone format. Use 'Z' or '+/-HHMM'.");
1005
0
    }
1006
0
  }
1007
1008
  std::chrono::system_clock::time_point Asn1TimeRecord::adjustTimezones(
1009
      const std::chrono::system_clock::time_point& value, const std::string& fromTimezone,
1010
      const std::string& toTimezone)
1011
0
  {
1012
0
    validateTimezone(fromTimezone);
1013
0
    validateTimezone(toTimezone);
1014
1015
0
    int fromOffsetSeconds = 0;
1016
0
    if (fromTimezone != "Z")
1017
0
    {
1018
0
      int fromSign = (fromTimezone[0] == '+') ? 1 : -1;
1019
0
      auto fromHours = std::stoi(fromTimezone.substr(1, 2));
1020
0
      auto fromMinutes = std::stoi(fromTimezone.substr(3, 2));
1021
0
      fromOffsetSeconds = fromSign * (fromHours * 3600 + fromMinutes * 60);
1022
0
    }
1023
1024
0
    int toOffsetSeconds = 0;
1025
0
    if (toTimezone != "Z")
1026
0
    {
1027
0
      int toSign = (toTimezone[0] == '+') ? 1 : -1;
1028
0
      auto toHours = std::stoi(toTimezone.substr(1, 2));
1029
0
      auto toMinutes = std::stoi(toTimezone.substr(3, 2));
1030
0
      toOffsetSeconds = toSign * (toHours * 3600 + toMinutes * 60);
1031
0
    }
1032
1033
0
    return value + std::chrono::seconds(toOffsetSeconds - fromOffsetSeconds);
1034
0
  }
1035
1036
  Asn1UtcTimeRecord::Asn1UtcTimeRecord(const std::chrono::system_clock::time_point& value, bool withSeconds)
1037
0
      : Asn1TimeRecord(Asn1UniversalTagType::UTCTime, value, "Z"), m_WithSeconds(withSeconds)
1038
0
  {
1039
0
    m_ValueLength = 11;
1040
0
    if (withSeconds)
1041
0
    {
1042
0
      m_ValueLength += 2;
1043
0
    }
1044
1045
0
    m_TotalLength = m_ValueLength + 2;
1046
0
  }
1047
1048
  void Asn1UtcTimeRecord::decodeValue(uint8_t const* data) const
1049
5
  {
1050
5
    std::string timeString(reinterpret_cast<const char*>(data), m_ValueLength);
1051
1052
5
    if (timeString.back() == 'Z')
1053
0
    {
1054
0
      timeString.pop_back();
1055
0
    }
1056
1057
5
    m_WithSeconds = true;
1058
5
    if (timeString.size() == 10)
1059
0
    {
1060
0
      m_WithSeconds = false;
1061
0
      timeString.append("00");
1062
0
    }
1063
1064
5
    auto year = std::stoi(timeString.substr(0, 2));
1065
5
    if (year <= 50)
1066
0
    {
1067
0
      timeString.insert(0, "20");
1068
0
    }
1069
5
    else
1070
5
    {
1071
5
      timeString.insert(0, "19");
1072
5
    }
1073
1074
5
    std::tm tm = {};
1075
5
    std::istringstream sstream(timeString);
1076
5
    sstream >> std::get_time(&tm, "%Y%m%d%H%M%S");
1077
1078
5
    if (sstream.fail())
1079
0
    {
1080
0
      throw std::runtime_error("Failed to parse ASN.1 UTC time");
1081
0
    }
1082
1083
5
    std::time_t timeValue = mkUtcTime(tm);
1084
5
    m_Value = std::chrono::system_clock::from_time_t(timeValue);
1085
5
  }
1086
1087
  std::vector<uint8_t> Asn1UtcTimeRecord::encodeValue() const
1088
0
  {
1089
0
    auto timeValue = std::chrono::system_clock::to_time_t(m_Value);
1090
1091
0
    auto tm = *std::gmtime(&timeValue);
1092
1093
0
    auto pattern = std::string("%y%m%d%H%M") + (m_WithSeconds ? "%S" : "");
1094
0
    std::ostringstream osstream;
1095
0
    osstream << std::put_time(&tm, pattern.c_str()) << 'Z';
1096
1097
0
    auto timeString = osstream.str();
1098
0
    return { timeString.begin(), timeString.end() };
1099
0
  }
1100
1101
  Asn1GeneralizedTimeRecord::Asn1GeneralizedTimeRecord(const std::chrono::system_clock::time_point& value,
1102
                                                       const std::string& timezone)
1103
0
      : Asn1TimeRecord(Asn1UniversalTagType::GeneralizedTime, value, timezone), m_Timezone(timezone)
1104
0
  {
1105
0
    m_ValueLength = 14 + (timezone == "Z" ? 1 : 5);
1106
1107
0
    auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(value.time_since_epoch()).count();
1108
0
    if (milliseconds % 1000 != 0)
1109
0
    {
1110
0
      m_ValueLength += 4;
1111
0
    }
1112
1113
0
    m_TotalLength = m_ValueLength + 2;
1114
0
  }
1115
1116
  void Asn1GeneralizedTimeRecord::decodeValue(uint8_t const* data) const
1117
110
  {
1118
110
    std::string timeString(reinterpret_cast<const char*>(data), m_ValueLength);
1119
1120
110
    std::string timezone = "Z";
1121
110
    auto timezonePos = timeString.find_first_of("+-");
1122
110
    if (timeString.back() == 'Z')
1123
0
    {
1124
0
      timeString.pop_back();
1125
0
    }
1126
110
    else if (timezonePos != std::string::npos)
1127
0
    {
1128
0
      timezone = timeString.substr(timezonePos);
1129
0
      timeString.erase(timezonePos);
1130
0
    }
1131
1132
110
    std::tm tm = {};
1133
110
    std::istringstream sstream(timeString);
1134
110
    sstream >> std::get_time(&tm, "%Y%m%d%H%M%S");
1135
1136
110
    if (sstream.fail())
1137
110
    {
1138
110
      throw std::runtime_error("Failed to parse ASN.1 generalized time");
1139
110
    }
1140
1141
0
    size_t dotPos = timeString.find('.');
1142
0
    int milliseconds = 0;
1143
0
    if (dotPos != std::string::npos)
1144
0
    {
1145
0
      std::string millisecondsStr = timeString.substr(dotPos + 1);
1146
      // Limit the milliseconds to 3 digits
1147
0
      if (millisecondsStr.length() > 3)
1148
0
      {
1149
0
        timeString.erase(timezonePos);
1150
0
        millisecondsStr.resize(3);
1151
0
      }
1152
0
      milliseconds = std::stoi(millisecondsStr);
1153
0
    }
1154
1155
0
    auto timeValue = mkUtcTime(tm);
1156
1157
0
    m_Timezone = timezone;
1158
0
    m_Value = adjustTimezones(
1159
0
        std::chrono::system_clock::from_time_t(timeValue) + std::chrono::milliseconds(milliseconds), timezone, "Z");
1160
0
  }
1161
1162
  std::vector<uint8_t> Asn1GeneralizedTimeRecord::encodeValue() const
1163
0
  {
1164
0
    auto value = adjustTimezones(m_Value, "Z", m_Timezone);
1165
0
    auto timeValue = std::chrono::system_clock::to_time_t(value);
1166
1167
0
    auto tm = *std::gmtime(&timeValue);
1168
1169
0
    auto pattern = std::string("%Y%m%d%H%M%S");
1170
0
    std::ostringstream osstream;
1171
0
    osstream << std::put_time(&tm, pattern.c_str());
1172
1173
0
    auto milliseconds =
1174
0
        std::chrono::duration_cast<std::chrono::milliseconds>(value.time_since_epoch()).count() % 1000;
1175
0
    if (milliseconds != 0)
1176
0
    {
1177
0
      osstream << "." << std::setw(3) << std::setfill('0') << milliseconds;
1178
0
    }
1179
1180
0
    osstream << m_Timezone;
1181
1182
0
    auto timeString = osstream.str();
1183
0
    return { timeString.begin(), timeString.end() };
1184
0
  }
1185
1186
  void Asn1BitStringRecord::BitSet::initFromString(const std::string& value)
1187
0
  {
1188
0
    m_NumBits = value.length();
1189
1190
0
    size_t numBytes = (m_NumBits + 7) / 8;
1191
0
    m_Data.clear();
1192
0
    m_Data.reserve(numBytes);
1193
1194
0
    size_t i = 0;
1195
0
    while (i < value.length())
1196
0
    {
1197
0
      std::string curByteString = value.substr(i, 8);
1198
0
      curByteString.append(8 - curByteString.length(), '0');
1199
0
      try
1200
0
      {
1201
0
        std::bitset<8> bs(curByteString);
1202
0
        m_Data.push_back(bs);
1203
0
        i += 8;
1204
0
      }
1205
0
      catch (const std::invalid_argument&)
1206
0
      {
1207
0
        throw std::invalid_argument("Invalid bit string");
1208
0
      }
1209
0
    }
1210
0
  }
1211
1212
  Asn1BitStringRecord::BitSet::BitSet(const std::string& value)
1213
0
  {
1214
0
    initFromString(value);
1215
0
  }
1216
1217
172
  Asn1BitStringRecord::BitSet::BitSet(const uint8_t* data, size_t numBits) : m_NumBits(numBits)
1218
172
  {
1219
172
    if (!data || !numBits)
1220
15
    {
1221
15
      throw std::invalid_argument("Provided data is null or num of bits is 0");
1222
15
    }
1223
1224
157
    size_t requiredBytes = (m_NumBits + 7) / 8;
1225
157
    m_Data.resize(requiredBytes);
1226
157
    std::copy_n(data, requiredBytes, m_Data.begin());
1227
157
  }
1228
1229
  Asn1BitStringRecord::BitSet& Asn1BitStringRecord::BitSet::operator=(const std::string& value)
1230
0
  {
1231
0
    initFromString(value);
1232
0
    return *this;
1233
0
  }
1234
1235
  std::string Asn1BitStringRecord::BitSet::toString() const
1236
0
  {
1237
0
    std::string result;
1238
0
    result.reserve(m_Data.size() * 8);
1239
0
    for (const auto bs : m_Data)
1240
0
    {
1241
0
      result += bs.to_string();
1242
0
    }
1243
0
    result.resize(m_NumBits);
1244
0
    return result;
1245
0
  }
1246
1247
  std::vector<uint8_t> Asn1BitStringRecord::BitSet::toBytes() const
1248
0
  {
1249
0
    std::vector<uint8_t> result;
1250
0
    result.reserve(m_Data.size());
1251
0
    for (const auto& bs : m_Data)
1252
0
    {
1253
0
      result.push_back(static_cast<uint8_t>(bs.to_ulong()));
1254
0
    }
1255
1256
0
    return result;
1257
0
  }
1258
1259
  size_t Asn1BitStringRecord::BitSet::sizeInBytes() const
1260
0
  {
1261
0
    return m_Data.size();
1262
0
  }
1263
1264
  Asn1BitStringRecord::Asn1BitStringRecord(const std::string& value)
1265
0
      : Asn1PrimitiveRecord(Asn1UniversalTagType::BitString)
1266
0
  {
1267
0
    m_Value = value;
1268
0
    m_ValueLength = m_Value.sizeInBytes() + 1;
1269
0
    m_TotalLength = m_ValueLength + 2;
1270
0
  }
1271
1272
  void Asn1BitStringRecord::decodeValue(uint8_t const* data) const
1273
172
  {
1274
172
    auto numBits = (m_ValueLength - 1) * 8 - static_cast<size_t>(data[0]);
1275
172
    m_Value = BitSet(data + 1, numBits);
1276
172
  }
1277
1278
  std::vector<uint8_t> Asn1BitStringRecord::encodeValue() const
1279
0
  {
1280
0
    auto result = m_Value.toBytes();
1281
0
    size_t unusedBits = m_Value.sizeInBytes() * 8 - m_Value.getNumBits();
1282
0
    result.insert(result.begin(), static_cast<uint8_t>(unusedBits));
1283
0
    return result;
1284
0
  }
1285
1286
  std::vector<std::string> Asn1BitStringRecord::toStringList() const
1287
0
  {
1288
0
    return { Asn1Record::toStringList().front() + ", Value: " + m_Value.toString() };
1289
0
  }
1290
1291
}  // namespace pcpp