/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 |