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