Coverage Report

Created: 2025-10-10 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/src/Asn1Codec.cpp
Line
Count
Source
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
365k
  {
94
365k
    uint8_t tagLen;
95
365k
    auto decodedRecord = decodeTagAndCreateRecord(data, dataLen, tagLen);
96
97
365k
    uint8_t lengthLen;
98
365k
    lengthLen = decodedRecord->decodeLength(data + tagLen, dataLen - tagLen);
99
100
365k
    decodedRecord->m_TotalLength = tagLen + lengthLen + decodedRecord->m_ValueLength;
101
365k
    if (decodedRecord->m_TotalLength < decodedRecord->m_ValueLength ||  // check for overflow
102
364k
        decodedRecord->m_TotalLength > dataLen)
103
2.78k
    {
104
2.78k
      throw std::invalid_argument("Cannot decode ASN.1 record, data doesn't contain the entire record");
105
2.78k
    }
106
107
362k
    uint8_t const* startOfData = data + tagLen + lengthLen;
108
362k
    internal::Asn1LoadPolicy policy = lazy ? internal::Asn1LoadPolicy::Lazy : internal::Asn1LoadPolicy::Eager;
109
362k
    decodedRecord->setEncodedValue(startOfData, policy);
110
111
362k
    return decodedRecord;
112
365k
  }
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
365k
  {
208
365k
    if (dataLen < 1)
209
0
    {
210
0
      throw std::invalid_argument("Cannot decode ASN.1 record tag");
211
0
    }
212
213
365k
    tagLen = 1;
214
215
365k
    Asn1TagClass tagClass = Asn1TagClass::Universal;
216
217
    // Check first 2 bits
218
365k
    auto tagClassBits = data[0] & 0xc0;
219
365k
    if (tagClassBits == 0)
220
288k
    {
221
288k
      tagClass = Asn1TagClass::Universal;
222
288k
    }
223
77.4k
    else if ((tagClassBits & 0xc0) == 0xc0)
224
1.15k
    {
225
1.15k
      tagClass = Asn1TagClass::Private;
226
1.15k
    }
227
76.2k
    else if ((tagClassBits & 0x80) == 0x80)
228
25.3k
    {
229
25.3k
      tagClass = Asn1TagClass::ContextSpecific;
230
25.3k
    }
231
50.9k
    else if ((tagClassBits & 0x40) == 0x40)
232
50.9k
    {
233
50.9k
      tagClass = Asn1TagClass::Application;
234
50.9k
    }
235
236
    // Check bit 6
237
365k
    auto tagTypeBits = data[0] & 0x20;
238
365k
    bool isConstructed = (tagTypeBits != 0);
239
240
    // Check last 5 bits
241
365k
    auto tagType = data[0] & 0x1f;
242
365k
    if (tagType == 0x1f)
243
596
    {
244
596
      if (dataLen < 2)
245
5
      {
246
5
        throw std::invalid_argument("Cannot decode ASN.1 record tag");
247
5
      }
248
249
591
      if ((data[1] & 0x80) != 0)
250
99
      {
251
99
        throw std::invalid_argument("ASN.1 tags with value larger than 127 are not supported");
252
99
      }
253
254
492
      tagType = data[1] & 0x7f;
255
492
      tagLen = 2;
256
492
    }
257
258
365k
    std::unique_ptr<Asn1Record> newRecord;
259
260
365k
    if (isConstructed)
261
147k
    {
262
147k
      if (tagClass == Asn1TagClass::Universal)
263
83.5k
      {
264
83.5k
        switch (static_cast<Asn1UniversalTagType>(tagType))
265
83.5k
        {
266
73.0k
        case Asn1UniversalTagType::Sequence:
267
73.0k
        {
268
73.0k
          newRecord.reset(new Asn1SequenceRecord());
269
73.0k
          break;
270
0
        }
271
9.22k
        case Asn1UniversalTagType::Set:
272
9.22k
        {
273
9.22k
          newRecord.reset(new Asn1SetRecord());
274
9.22k
          break;
275
0
        }
276
1.29k
        default:
277
1.29k
        {
278
1.29k
          newRecord.reset(new Asn1ConstructedRecord());
279
1.29k
        }
280
83.5k
        }
281
83.5k
      }
282
64.3k
      else
283
64.3k
      {
284
64.3k
        newRecord.reset(new Asn1ConstructedRecord());
285
64.3k
      }
286
147k
    }
287
217k
    else
288
217k
    {
289
217k
      if (tagClass == Asn1TagClass::Universal)
290
204k
      {
291
204k
        auto asn1UniversalTagType = static_cast<Asn1UniversalTagType>(tagType);
292
204k
        switch (asn1UniversalTagType)
293
204k
        {
294
59.8k
        case Asn1UniversalTagType::Integer:
295
59.8k
        {
296
59.8k
          newRecord.reset(new Asn1IntegerRecord());
297
59.8k
          break;
298
0
        }
299
20.3k
        case Asn1UniversalTagType::Enumerated:
300
20.3k
        {
301
20.3k
          newRecord.reset(new Asn1EnumeratedRecord());
302
20.3k
          break;
303
0
        }
304
115k
        case Asn1UniversalTagType::OctetString:
305
115k
        {
306
115k
          newRecord.reset(new Asn1OctetStringRecord());
307
115k
          break;
308
0
        }
309
139
        case Asn1UniversalTagType::UTF8String:
310
139
        {
311
139
          newRecord.reset(new Asn1UTF8StringRecord());
312
139
          break;
313
0
        }
314
85
        case Asn1UniversalTagType::PrintableString:
315
85
        {
316
85
          newRecord.reset(new Asn1PrintableStringRecord());
317
85
          break;
318
0
        }
319
34
        case Asn1UniversalTagType::IA5String:
320
34
        {
321
34
          newRecord.reset(new Asn1IA5StringRecord());
322
34
          break;
323
0
        }
324
4.27k
        case Asn1UniversalTagType::Boolean:
325
4.27k
        {
326
4.27k
          newRecord.reset(new Asn1BooleanRecord());
327
4.27k
          break;
328
0
        }
329
312
        case Asn1UniversalTagType::BitString:
330
312
        {
331
312
          newRecord.reset(new Asn1BitStringRecord());
332
312
          break;
333
0
        }
334
182
        case Asn1UniversalTagType::Null:
335
182
        {
336
182
          newRecord.reset(new Asn1NullRecord());
337
182
          break;
338
0
        }
339
644
        case Asn1UniversalTagType::ObjectIdentifier:
340
644
        {
341
644
          newRecord.reset(new Asn1ObjectIdentifierRecord());
342
644
          break;
343
0
        }
344
35
        case Asn1UniversalTagType::UTCTime:
345
35
        {
346
35
          newRecord.reset(new Asn1UtcTimeRecord());
347
35
          break;
348
0
        }
349
255
        case Asn1UniversalTagType::GeneralizedTime:
350
255
        {
351
255
          newRecord.reset(new Asn1GeneralizedTimeRecord());
352
255
          break;
353
0
        }
354
3.09k
        default:
355
3.09k
        {
356
3.09k
          newRecord.reset(new Asn1GenericRecord());
357
3.09k
        }
358
204k
        }
359
204k
      }
360
12.9k
      else
361
12.9k
      {
362
12.9k
        newRecord.reset(new Asn1GenericRecord());
363
12.9k
      }
364
217k
    }
365
366
365k
    newRecord->m_TagClass = tagClass;
367
365k
    newRecord->m_IsConstructed = isConstructed;
368
365k
    newRecord->m_TagType = tagType;
369
370
365k
    return newRecord;
371
365k
  }
372
373
  uint8_t Asn1Record::decodeLength(const uint8_t* data, size_t dataLen)
374
365k
  {
375
365k
    if (dataLen < 1)
376
180
    {
377
180
      throw std::invalid_argument("Cannot decode ASN.1 record length");
378
180
    }
379
380
    // Check 8th bit
381
365k
    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
365k
    if (lengthForm == 0)
387
265k
    {
388
265k
      m_ValueLength = data[0];
389
265k
      return 1;
390
265k
    }
391
392
99.3k
    uint8_t actualLengthBytes = data[0] & 0x7F;
393
99.3k
    const uint8_t* actualLengthData = data + 1;
394
395
99.3k
    if (dataLen < static_cast<size_t>(actualLengthBytes) + 1)
396
276
    {
397
276
      throw std::invalid_argument("Cannot decode ASN.1 record length");
398
276
    }
399
400
463k
    for (int i = 0; i < actualLengthBytes; i++)
401
365k
    {
402
365k
      size_t partialValueLength = m_ValueLength << 8;
403
365k
      if (partialValueLength < m_ValueLength)  // check for overflow
404
609
      {
405
609
        throw std::invalid_argument("Cannot decode ASN.1 record length");
406
609
      }
407
408
364k
      m_ValueLength = partialValueLength | actualLengthData[i];
409
364k
    }
410
411
98.4k
    return 1 + actualLengthBytes;
412
99.0k
  }
413
414
  void Asn1Record::decodeValueIfNeeded() const
415
398k
  {
416
    // TODO: This is not thread-safe and can cause issues in a multiple reader scenario.
417
398k
    if (m_EncodedValue != nullptr)
418
360k
    {
419
360k
      decodeValue(m_EncodedValue);
420
360k
      m_EncodedValue = nullptr;  // Clear the encoded value after decoding
421
360k
    }
422
398k
  }
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
361k
  {
463
361k
    m_EncodedValue = dataSource;
464
465
361k
    if (loadPolicy == internal::Asn1LoadPolicy::Eager)
466
308k
    {
467
308k
      decodeValueIfNeeded();
468
308k
    }
469
361k
  }
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
15.1k
  {
485
15.1k
    m_Value = std::make_unique<uint8_t[]>(m_ValueLength);
486
15.1k
    std::memcpy(m_Value.get(), data, m_ValueLength);
487
15.1k
  }
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
144k
  {
520
144k
    if (!(data || m_ValueLength))
521
0
    {
522
0
      return;
523
0
    }
524
525
144k
    auto value = data;
526
144k
    auto valueLen = m_ValueLength;
527
528
456k
    while (valueLen > 0)
529
311k
    {
530
311k
      auto subRecord = Asn1Record::decode(value, valueLen, LazySubRecordDecoding);
531
311k
      value += subRecord->getTotalLength();
532
311k
      valueLen -= subRecord->getTotalLength();
533
534
311k
      m_SubRecords.pushBack(std::move(subRecord));
535
311k
    }
536
144k
  }
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
115k
  Asn1PrimitiveRecord::Asn1PrimitiveRecord(Asn1UniversalTagType tagType) : Asn1Record()
584
115k
  {
585
115k
    m_TagType = static_cast<uint8_t>(tagType);
586
115k
    m_TagClass = Asn1TagClass::Universal;
587
115k
    m_IsConstructed = false;
588
115k
  }
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
79.8k
  {
602
79.8k
    std::string valueStr = value;
603
604
    // Optional 0x or 0X prefix
605
79.8k
    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
79.8k
    if (valueStr.empty())
611
20
    {
612
20
      throw std::invalid_argument("Value is not a valid hex stream");
613
20
    }
614
615
183k
    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
79.8k
    return valueStr;
621
79.8k
  }
622
623
  Asn1IntegerRecord::BigInt& Asn1IntegerRecord::BigInt::operator=(const std::string& value)
624
79.8k
  {
625
79.8k
    m_Value = initFromString(value);
626
79.8k
    return *this;
627
79.8k
  }
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(bool removeLeadingZeros) const
635
0
  {
636
0
    if (!removeLeadingZeros)
637
0
    {
638
0
      return m_Value;
639
0
    }
640
641
0
    auto firstNonZero = m_Value.find_first_not_of('0');
642
0
    if (firstNonZero == std::string::npos)
643
0
    {
644
0
      return "0";
645
0
    }
646
647
0
    return m_Value.substr(firstNonZero);
648
0
  }
649
650
  std::vector<uint8_t> Asn1IntegerRecord::BigInt::toBytes() const
651
0
  {
652
0
    std::string value = m_Value;
653
0
    if (m_Value.size() % 2 != 0)
654
0
    {
655
0
      value.insert(0, 1, '0');
656
0
    }
657
658
0
    std::vector<uint8_t> result;
659
0
    for (std::size_t i = 0; i < value.size(); i += 2)
660
0
    {
661
0
      std::string byteStr = value.substr(i, 2);
662
0
      auto byte = static_cast<uint8_t>(std::stoul(byteStr, nullptr, 16));
663
0
      result.push_back(byte);
664
0
    }
665
666
0
    return result;
667
0
  }
668
669
0
  Asn1IntegerRecord::Asn1IntegerRecord(uint64_t value) : Asn1PrimitiveRecord(Asn1UniversalTagType::Integer)
670
0
  {
671
0
    m_Value = value;
672
673
0
    std::size_t length = 0;
674
0
    while (value != 0)
675
0
    {
676
0
      ++length;
677
0
      value >>= 8;
678
0
    }
679
0
    m_ValueLength = length == 0 ? 1 : length;
680
681
0
    m_TotalLength = m_ValueLength + 2;
682
0
  }
683
684
0
  Asn1IntegerRecord::Asn1IntegerRecord(const std::string& value) : Asn1PrimitiveRecord(Asn1UniversalTagType::Integer)
685
0
  {
686
0
    m_Value = value;
687
0
    m_ValueLength = m_Value.size();
688
0
    m_TotalLength = m_ValueLength + 2;
689
0
  }
690
691
  void Asn1IntegerRecord::decodeValue(uint8_t const* data) const
692
79.8k
  {
693
79.8k
    m_Value = pcpp::byteArrayToHexString(data, m_ValueLength);
694
79.8k
  }
695
696
  std::vector<uint8_t> Asn1IntegerRecord::encodeValue() const
697
0
  {
698
0
    return m_Value.toBytes();
699
0
  }
700
701
  std::vector<std::string> Asn1IntegerRecord::toStringList() const
702
0
  {
703
0
    auto valueAsString =
704
0
        m_Value.canFit<uint64_t>() ? std::to_string(getIntValue<uint64_t>()) : "0x" + getValueAsString();
705
0
    return std::vector<std::string>({ Asn1Record::toStringList().front() + ", Value: " + valueAsString });
706
0
  }
707
708
0
  Asn1EnumeratedRecord::Asn1EnumeratedRecord(uint32_t value) : Asn1IntegerRecord(value)
709
0
  {
710
0
    m_TagType = static_cast<uint8_t>(Asn1UniversalTagType::Enumerated);
711
0
  }
712
713
0
  Asn1OctetStringRecord::Asn1OctetStringRecord(const uint8_t* value, size_t valueLength) : m_IsPrintable(false)
714
0
  {
715
0
    m_Value = byteArrayToHexString(value, valueLength);
716
0
    m_ValueLength = valueLength;
717
0
    m_TotalLength = m_ValueLength + 2;
718
0
  }
719
720
  void Asn1OctetStringRecord::decodeValue(uint8_t const* data) const
721
114k
  {
722
114k
    auto value = reinterpret_cast<char const*>(data);
723
724
2.66M
    m_IsPrintable = std::all_of(value, value + m_ValueLength, [](char c) { return isprint(0xff & c); });
725
726
114k
    if (m_IsPrintable)
727
101k
    {
728
101k
      Asn1StringRecord::decodeValue(data);
729
101k
    }
730
12.9k
    else
731
12.9k
    {
732
12.9k
      m_Value = byteArrayToHexString(data, m_ValueLength);
733
12.9k
    }
734
114k
  }
735
736
  std::vector<uint8_t> Asn1OctetStringRecord::encodeValue() const
737
0
  {
738
0
    if (m_IsPrintable)
739
0
    {
740
0
      return Asn1StringRecord::encodeValue();
741
0
    }
742
743
    // converting the hex stream to a byte array.
744
    // The byte array size is half the size of the string
745
    // i.e "1a2b" (length == 4)  becomes {0x1a, 0x2b} (length == 2)
746
0
    auto rawValueSize = static_cast<size_t>(m_Value.size() / 2);
747
0
    std::vector<uint8_t> rawValue;
748
0
    rawValue.resize(rawValueSize);
749
0
    hexStringToByteArray(m_Value, rawValue.data(), rawValueSize);
750
0
    return rawValue;
751
0
  }
752
753
0
  Asn1BooleanRecord::Asn1BooleanRecord(bool value) : Asn1PrimitiveRecord(Asn1UniversalTagType::Boolean)
754
0
  {
755
0
    m_Value = value;
756
0
    m_ValueLength = 1;
757
0
    m_TotalLength = 3;
758
0
  }
759
760
  void Asn1BooleanRecord::decodeValue(uint8_t const* data) const
761
4.25k
  {
762
4.25k
    m_Value = data[0] != 0;
763
4.25k
  }
764
765
  std::vector<uint8_t> Asn1BooleanRecord::encodeValue() const
766
0
  {
767
0
    uint8_t byte = (m_Value ? 0xff : 0x00);
768
0
    return { byte };
769
0
  }
770
771
  std::vector<std::string> Asn1BooleanRecord::toStringList() const
772
0
  {
773
0
    return { Asn1Record::toStringList().front() + ", Value: " + (getValue() ? "true" : "false") };
774
0
  }
775
776
182
  Asn1NullRecord::Asn1NullRecord() : Asn1PrimitiveRecord(Asn1UniversalTagType::Null)
777
182
  {
778
182
    m_ValueLength = 0;
779
182
    m_TotalLength = 2;
780
182
  }
781
782
  Asn1ObjectIdentifier::Asn1ObjectIdentifier(const uint8_t* data, size_t dataLen)
783
604
  {
784
    // A description of OID encoding can be found here:
785
    // https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-object-identifier?redirectedfrom=MSDN
786
787
604
    if (!data || dataLen == 0)
788
80
    {
789
80
      throw std::invalid_argument("Malformed OID: Not enough bytes for the first component");
790
80
    }
791
792
524
    size_t currentByteIndex = 0;
793
524
    std::vector<uint32_t> components;
794
795
524
    uint8_t firstByte = data[currentByteIndex++];
796
    // Decode the first byte: first_component * 40 + second_component
797
524
    components.push_back(static_cast<uint32_t>(firstByte / 40));
798
524
    components.push_back(static_cast<uint32_t>(firstByte % 40));
799
800
524
    uint32_t currentComponentValue = 0;
801
524
    bool componentStarted = false;
802
803
    // Process remaining bytes using base-128 encoding
804
9.25k
    while (currentByteIndex < dataLen)
805
8.73k
    {
806
8.73k
      uint8_t byte = data[currentByteIndex++];
807
808
      // Shift previous bits left by 7 and append lower 7 bits
809
8.73k
      currentComponentValue = (currentComponentValue << 7) | (byte & 0x7f);
810
8.73k
      componentStarted = true;
811
812
      // If the MSB is 0, this is the final byte of the current value
813
8.73k
      if ((byte & 0x80) == 0)
814
7.41k
      {
815
7.41k
        components.push_back(currentComponentValue);
816
7.41k
        currentComponentValue = 0;
817
7.41k
        componentStarted = false;
818
7.41k
      }
819
8.73k
    }
820
821
524
    if (componentStarted)
822
50
    {
823
50
      throw std::invalid_argument("Malformed OID: Incomplete component at end of data");
824
50
    }
825
826
474
    m_Components = components;
827
474
  }
828
829
  Asn1ObjectIdentifier::Asn1ObjectIdentifier(const std::string& oidString)
830
0
  {
831
0
    std::vector<uint32_t> components;
832
0
    std::istringstream stream(oidString);
833
0
    std::string token;
834
835
0
    while (std::getline(stream, token, '.'))
836
0
    {
837
0
      if (token.empty())
838
0
      {
839
0
        throw std::invalid_argument("Malformed OID: empty component");
840
0
      }
841
842
0
      unsigned long long value;
843
0
      try
844
0
      {
845
0
        value = std::stoull(token);
846
0
      }
847
0
      catch (const std::exception&)
848
0
      {
849
0
        throw std::invalid_argument("Malformed OID: invalid component");
850
0
      }
851
852
0
      if (value > std::numeric_limits<uint32_t>::max())
853
0
      {
854
0
        throw std::invalid_argument("Malformed OID: component out of uint32_t range");
855
0
      }
856
857
0
      components.push_back(static_cast<uint32_t>(value));
858
0
    }
859
860
0
    if (components.size() < 2)
861
0
    {
862
0
      throw std::invalid_argument("Malformed OID: an OID must have at least two components");
863
0
    }
864
865
0
    if (components[0] > 2)
866
0
    {
867
0
      throw std::invalid_argument("Malformed OID: first component must be 0, 1, or 2");
868
0
    }
869
870
0
    if ((components[0] == 0 || components[0] == 1) && components[1] >= 40)
871
0
    {
872
0
      throw std::invalid_argument(
873
0
          "Malformed OID: second component must be less than 40 when first component is 0 or 1");
874
0
    }
875
876
0
    m_Components = components;
877
0
  }
878
879
  std::string Asn1ObjectIdentifier::toString() const
880
0
  {
881
0
    if (m_Components.empty())
882
0
    {
883
0
      return "";
884
0
    }
885
886
0
    std::ostringstream stream;
887
0
    stream << m_Components[0];
888
889
0
    for (size_t i = 1; i < m_Components.size(); ++i)
890
0
    {
891
0
      stream << "." << m_Components[i];
892
0
    }
893
0
    return stream.str();
894
0
  }
895
896
  std::vector<uint8_t> Asn1ObjectIdentifier::toBytes() const
897
0
  {
898
    // A description of OID encoding can be found here:
899
    // https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-object-identifier?redirectedfrom=MSDN
900
901
0
    if (m_Components.size() < 2)
902
0
    {
903
0
      throw std::runtime_error("OID must have at least two components to encode.");
904
0
    }
905
906
0
    std::vector<uint8_t> encoded;
907
908
    // Encode the first two components into one byte
909
0
    uint32_t firstComponent = m_Components[0];
910
0
    uint32_t secondComponent = m_Components[1];
911
0
    encoded.push_back(static_cast<uint8_t>(firstComponent * 40 + secondComponent));
912
913
    // Encode remaining components using base-128 encoding
914
0
    for (size_t i = 2; i < m_Components.size(); ++i)
915
0
    {
916
0
      uint32_t currentComponent = m_Components[i];
917
0
      std::vector<uint8_t> temp;
918
919
      // At least one byte must be generated even if value is 0
920
0
      do
921
0
      {
922
0
        temp.push_back(static_cast<uint8_t>(currentComponent & 0x7F));
923
0
        currentComponent >>= 7;
924
0
      } while (currentComponent > 0);
925
926
      // Set continuation bits (MSB) for all but the last byte
927
0
      for (size_t j = temp.size(); j-- > 0;)
928
0
      {
929
0
        uint8_t byte = temp[j];
930
0
        if (j != 0)
931
0
        {
932
0
          byte |= 0x80;
933
0
        }
934
0
        encoded.push_back(byte);
935
0
      }
936
0
    }
937
938
0
    return encoded;
939
0
  }
940
941
  Asn1ObjectIdentifierRecord::Asn1ObjectIdentifierRecord(const Asn1ObjectIdentifier& value)
942
0
      : Asn1PrimitiveRecord(Asn1UniversalTagType::ObjectIdentifier)
943
0
  {
944
0
    m_Value = value;
945
0
    m_ValueLength = value.toBytes().size();
946
0
    m_TotalLength = m_ValueLength + 2;
947
0
  }
948
949
  void Asn1ObjectIdentifierRecord::decodeValue(uint8_t const* data) const
950
604
  {
951
604
    m_Value = Asn1ObjectIdentifier(data, m_ValueLength);
952
604
  }
953
954
  std::vector<uint8_t> Asn1ObjectIdentifierRecord::encodeValue() const
955
0
  {
956
0
    return m_Value.toBytes();
957
0
  }
958
959
  std::vector<std::string> Asn1ObjectIdentifierRecord::toStringList() const
960
0
  {
961
0
    return { Asn1Record::toStringList().front() + ", Value: " + getValue().toString() };
962
0
  }
963
964
  Asn1TimeRecord::Asn1TimeRecord(Asn1UniversalTagType tagType, const std::chrono::system_clock::time_point& value,
965
                                 const std::string& timezone)
966
0
      : Asn1PrimitiveRecord(tagType)
967
0
  {
968
0
    validateTimezone(timezone);
969
0
    m_Value = adjustTimezones(value, timezone, "Z");
970
0
  }
971
972
  std::string Asn1TimeRecord::getValueAsString(const std::string& format, const std::string& timezone,
973
                                               bool includeMilliseconds) const
974
0
  {
975
0
    auto value = getValue(timezone);
976
0
    auto timeValue = std::chrono::system_clock::to_time_t(value);
977
0
    auto tmValue = *std::gmtime(&timeValue);
978
979
0
    std::ostringstream osstream;
980
0
    osstream << std::put_time(&tmValue, format.c_str());
981
982
0
    if (includeMilliseconds)
983
0
    {
984
0
      auto milliseconds =
985
0
          std::chrono::duration_cast<std::chrono::milliseconds>(value.time_since_epoch()).count() % 1000;
986
0
      if (milliseconds != 0)
987
0
      {
988
0
        osstream << "." << std::setw(3) << std::setfill('0') << milliseconds;
989
0
      }
990
0
    }
991
992
0
    if (timezone != "Z")
993
0
    {
994
0
      osstream << " UTC" << timezone;
995
0
    }
996
997
0
    return osstream.str();
998
0
  }
999
1000
  std::vector<std::string> Asn1TimeRecord::toStringList() const
1001
0
  {
1002
0
    return { Asn1Record::toStringList().front() + ", Value: " + getValueAsString("%Y-%m-%d %H:%M:%S", "Z", true) };
1003
0
  }
1004
1005
  void Asn1TimeRecord::validateTimezone(const std::string& timezone)
1006
0
  {
1007
0
    if (timezone == "Z")
1008
0
    {
1009
0
      return;
1010
0
    }
1011
1012
0
    if (timezone.length() != 5 || (timezone[0] != '+' && timezone[0] != '-') || !std::isdigit(timezone[1]) ||
1013
0
        !std::isdigit(timezone[2]) || !std::isdigit(timezone[3]) || !std::isdigit(timezone[4]))
1014
0
    {
1015
0
      throw std::invalid_argument("Invalid timezone format. Use 'Z' or '+/-HHMM'.");
1016
0
    }
1017
0
  }
1018
1019
  std::chrono::system_clock::time_point Asn1TimeRecord::adjustTimezones(
1020
      const std::chrono::system_clock::time_point& value, const std::string& fromTimezone,
1021
      const std::string& toTimezone)
1022
0
  {
1023
0
    validateTimezone(fromTimezone);
1024
0
    validateTimezone(toTimezone);
1025
1026
0
    int fromOffsetSeconds = 0;
1027
0
    if (fromTimezone != "Z")
1028
0
    {
1029
0
      int fromSign = (fromTimezone[0] == '+') ? 1 : -1;
1030
0
      auto fromHours = std::stoi(fromTimezone.substr(1, 2));
1031
0
      auto fromMinutes = std::stoi(fromTimezone.substr(3, 2));
1032
0
      fromOffsetSeconds = fromSign * (fromHours * 3600 + fromMinutes * 60);
1033
0
    }
1034
1035
0
    int toOffsetSeconds = 0;
1036
0
    if (toTimezone != "Z")
1037
0
    {
1038
0
      int toSign = (toTimezone[0] == '+') ? 1 : -1;
1039
0
      auto toHours = std::stoi(toTimezone.substr(1, 2));
1040
0
      auto toMinutes = std::stoi(toTimezone.substr(3, 2));
1041
0
      toOffsetSeconds = toSign * (toHours * 3600 + toMinutes * 60);
1042
0
    }
1043
1044
0
    return value + std::chrono::seconds(toOffsetSeconds - fromOffsetSeconds);
1045
0
  }
1046
1047
  Asn1UtcTimeRecord::Asn1UtcTimeRecord(const std::chrono::system_clock::time_point& value, bool withSeconds)
1048
0
      : Asn1TimeRecord(Asn1UniversalTagType::UTCTime, value, "Z"), m_WithSeconds(withSeconds)
1049
0
  {
1050
0
    m_ValueLength = 11;
1051
0
    if (withSeconds)
1052
0
    {
1053
0
      m_ValueLength += 2;
1054
0
    }
1055
1056
0
    m_TotalLength = m_ValueLength + 2;
1057
0
  }
1058
1059
  void Asn1UtcTimeRecord::decodeValue(uint8_t const* data) const
1060
10
  {
1061
10
    std::string timeString(reinterpret_cast<const char*>(data), m_ValueLength);
1062
1063
10
    if (timeString.back() == 'Z')
1064
0
    {
1065
0
      timeString.pop_back();
1066
0
    }
1067
1068
10
    m_WithSeconds = true;
1069
10
    if (timeString.size() == 10)
1070
0
    {
1071
0
      m_WithSeconds = false;
1072
0
      timeString.append("00");
1073
0
    }
1074
1075
10
    auto year = std::stoi(timeString.substr(0, 2));
1076
10
    if (year <= 50)
1077
0
    {
1078
0
      timeString.insert(0, "20");
1079
0
    }
1080
10
    else
1081
10
    {
1082
10
      timeString.insert(0, "19");
1083
10
    }
1084
1085
10
    std::tm tm = {};
1086
10
    std::istringstream sstream(timeString);
1087
10
    sstream >> std::get_time(&tm, "%Y%m%d%H%M%S");
1088
1089
10
    if (sstream.fail())
1090
0
    {
1091
0
      throw std::runtime_error("Failed to parse ASN.1 UTC time");
1092
0
    }
1093
1094
10
    std::time_t timeValue = mkUtcTime(tm);
1095
10
    m_Value = std::chrono::system_clock::from_time_t(timeValue);
1096
10
  }
1097
1098
  std::vector<uint8_t> Asn1UtcTimeRecord::encodeValue() const
1099
0
  {
1100
0
    auto timeValue = std::chrono::system_clock::to_time_t(m_Value);
1101
1102
0
    auto tm = *std::gmtime(&timeValue);
1103
1104
0
    auto pattern = std::string("%y%m%d%H%M") + (m_WithSeconds ? "%S" : "");
1105
0
    std::ostringstream osstream;
1106
0
    osstream << std::put_time(&tm, pattern.c_str()) << 'Z';
1107
1108
0
    auto timeString = osstream.str();
1109
0
    return { timeString.begin(), timeString.end() };
1110
0
  }
1111
1112
  Asn1GeneralizedTimeRecord::Asn1GeneralizedTimeRecord(const std::chrono::system_clock::time_point& value,
1113
                                                       const std::string& timezone)
1114
0
      : Asn1TimeRecord(Asn1UniversalTagType::GeneralizedTime, value, timezone), m_Timezone(timezone)
1115
0
  {
1116
0
    m_ValueLength = 14 + (timezone == "Z" ? 1 : 5);
1117
1118
0
    auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(value.time_since_epoch()).count();
1119
0
    if (milliseconds % 1000 != 0)
1120
0
    {
1121
0
      m_ValueLength += 4;
1122
0
    }
1123
1124
0
    m_TotalLength = m_ValueLength + 2;
1125
0
  }
1126
1127
  void Asn1GeneralizedTimeRecord::decodeValue(uint8_t const* data) const
1128
245
  {
1129
245
    std::string timeString(reinterpret_cast<const char*>(data), m_ValueLength);
1130
1131
245
    std::string timezone = "Z";
1132
245
    auto timezonePos = timeString.find_first_of("+-");
1133
245
    if (timeString.back() == 'Z')
1134
0
    {
1135
0
      timeString.pop_back();
1136
0
    }
1137
245
    else if (timezonePos != std::string::npos)
1138
75
    {
1139
75
      timezone = timeString.substr(timezonePos);
1140
75
      timeString.erase(timezonePos);
1141
75
    }
1142
1143
245
    std::tm tm = {};
1144
245
    std::istringstream sstream(timeString);
1145
245
    sstream >> std::get_time(&tm, "%Y%m%d%H%M%S");
1146
1147
245
    if (sstream.fail())
1148
245
    {
1149
245
      throw std::runtime_error("Failed to parse ASN.1 generalized time");
1150
245
    }
1151
1152
0
    size_t dotPos = timeString.find('.');
1153
0
    int milliseconds = 0;
1154
0
    if (dotPos != std::string::npos)
1155
0
    {
1156
0
      std::string millisecondsStr = timeString.substr(dotPos + 1);
1157
      // Limit the milliseconds to 3 digits
1158
0
      if (millisecondsStr.length() > 3)
1159
0
      {
1160
0
        timeString.erase(timezonePos);
1161
0
        millisecondsStr.resize(3);
1162
0
      }
1163
0
      milliseconds = std::stoi(millisecondsStr);
1164
0
    }
1165
1166
0
    auto timeValue = mkUtcTime(tm);
1167
1168
0
    m_Timezone = timezone;
1169
0
    m_Value = adjustTimezones(
1170
0
        std::chrono::system_clock::from_time_t(timeValue) + std::chrono::milliseconds(milliseconds), timezone, "Z");
1171
0
  }
1172
1173
  std::vector<uint8_t> Asn1GeneralizedTimeRecord::encodeValue() const
1174
0
  {
1175
0
    auto value = adjustTimezones(m_Value, "Z", m_Timezone);
1176
0
    auto timeValue = std::chrono::system_clock::to_time_t(value);
1177
1178
0
    auto tm = *std::gmtime(&timeValue);
1179
1180
0
    auto pattern = std::string("%Y%m%d%H%M%S");
1181
0
    std::ostringstream osstream;
1182
0
    osstream << std::put_time(&tm, pattern.c_str());
1183
1184
0
    auto milliseconds =
1185
0
        std::chrono::duration_cast<std::chrono::milliseconds>(value.time_since_epoch()).count() % 1000;
1186
0
    if (milliseconds != 0)
1187
0
    {
1188
0
      osstream << "." << std::setw(3) << std::setfill('0') << milliseconds;
1189
0
    }
1190
1191
0
    osstream << m_Timezone;
1192
1193
0
    auto timeString = osstream.str();
1194
0
    return { timeString.begin(), timeString.end() };
1195
0
  }
1196
1197
  void Asn1BitStringRecord::BitSet::initFromString(const std::string& value)
1198
0
  {
1199
0
    m_NumBits = value.length();
1200
1201
0
    size_t numBytes = (m_NumBits + 7) / 8;
1202
0
    m_Data.clear();
1203
0
    m_Data.reserve(numBytes);
1204
1205
0
    size_t i = 0;
1206
0
    while (i < value.length())
1207
0
    {
1208
0
      std::string curByteString = value.substr(i, 8);
1209
0
      curByteString.append(8 - curByteString.length(), '0');
1210
0
      try
1211
0
      {
1212
0
        std::bitset<8> bs(curByteString);
1213
0
        m_Data.push_back(bs);
1214
0
        i += 8;
1215
0
      }
1216
0
      catch (const std::invalid_argument&)
1217
0
      {
1218
0
        throw std::invalid_argument("Invalid bit string");
1219
0
      }
1220
0
    }
1221
0
  }
1222
1223
  Asn1BitStringRecord::BitSet::BitSet(const std::string& value)
1224
0
  {
1225
0
    initFromString(value);
1226
0
  }
1227
1228
292
  Asn1BitStringRecord::BitSet::BitSet(const uint8_t* data, size_t numBits) : m_NumBits(numBits)
1229
292
  {
1230
292
    if (!data || !numBits)
1231
5
    {
1232
5
      throw std::invalid_argument("Provided data is null or num of bits is 0");
1233
5
    }
1234
1235
287
    size_t requiredBytes = (m_NumBits + 7) / 8;
1236
287
    m_Data.resize(requiredBytes);
1237
287
    std::copy_n(data, requiredBytes, m_Data.begin());
1238
287
  }
1239
1240
  Asn1BitStringRecord::BitSet& Asn1BitStringRecord::BitSet::operator=(const std::string& value)
1241
0
  {
1242
0
    initFromString(value);
1243
0
    return *this;
1244
0
  }
1245
1246
  std::string Asn1BitStringRecord::BitSet::toString() const
1247
0
  {
1248
0
    std::string result;
1249
0
    result.reserve(m_Data.size() * 8);
1250
0
    for (const auto bs : m_Data)
1251
0
    {
1252
0
      result += bs.to_string();
1253
0
    }
1254
0
    result.resize(m_NumBits);
1255
0
    return result;
1256
0
  }
1257
1258
  std::vector<uint8_t> Asn1BitStringRecord::BitSet::toBytes() const
1259
0
  {
1260
0
    std::vector<uint8_t> result;
1261
0
    result.reserve(m_Data.size());
1262
0
    for (const auto& bs : m_Data)
1263
0
    {
1264
0
      result.push_back(static_cast<uint8_t>(bs.to_ulong()));
1265
0
    }
1266
1267
0
    return result;
1268
0
  }
1269
1270
  size_t Asn1BitStringRecord::BitSet::sizeInBytes() const
1271
0
  {
1272
0
    return m_Data.size();
1273
0
  }
1274
1275
  Asn1BitStringRecord::Asn1BitStringRecord(const std::string& value)
1276
0
      : Asn1PrimitiveRecord(Asn1UniversalTagType::BitString)
1277
0
  {
1278
0
    m_Value = value;
1279
0
    m_ValueLength = m_Value.sizeInBytes() + 1;
1280
0
    m_TotalLength = m_ValueLength + 2;
1281
0
  }
1282
1283
  void Asn1BitStringRecord::decodeValue(uint8_t const* data) const
1284
292
  {
1285
292
    auto numBits = (m_ValueLength - 1) * 8 - static_cast<size_t>(data[0]);
1286
292
    m_Value = BitSet(data + 1, numBits);
1287
292
  }
1288
1289
  std::vector<uint8_t> Asn1BitStringRecord::encodeValue() const
1290
0
  {
1291
0
    auto result = m_Value.toBytes();
1292
0
    size_t unusedBits = m_Value.sizeInBytes() * 8 - m_Value.getNumBits();
1293
0
    result.insert(result.begin(), static_cast<uint8_t>(unusedBits));
1294
0
    return result;
1295
0
  }
1296
1297
  std::vector<std::string> Asn1BitStringRecord::toStringList() const
1298
0
  {
1299
0
    return { Asn1Record::toStringList().front() + ", Value: " + m_Value.toString() };
1300
0
  }
1301
1302
}  // namespace pcpp