Skip to content

Commit 8b7fef4

Browse files
authored
Add decoding and encoding of ASN.1 string records (#1851)
1 parent 0588d88 commit 8b7fef4

File tree

3 files changed

+215
-33
lines changed

3 files changed

+215
-33
lines changed

Packet++/header/Asn1Codec.h

Lines changed: 95 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -492,40 +492,121 @@ namespace pcpp
492492
Asn1EnumeratedRecord() = default;
493493
};
494494

495+
/// @class Asn1StringRecord
496+
/// An abstract class for representing ASN.1 string records.
497+
/// This class is not instantiable, users should use the derived classes
498+
template <Asn1UniversalTagType TagType> class Asn1StringRecord : public Asn1PrimitiveRecord
499+
{
500+
public:
501+
/// @return The string value of this record
502+
std::string getValue()
503+
{
504+
decodeValueIfNeeded();
505+
return m_Value;
506+
};
507+
508+
protected:
509+
Asn1StringRecord() : Asn1PrimitiveRecord(TagType)
510+
{}
511+
512+
explicit Asn1StringRecord(const std::string& value) : Asn1PrimitiveRecord(TagType), m_Value(value)
513+
{
514+
m_ValueLength = value.size();
515+
m_TotalLength = m_ValueLength + 2;
516+
}
517+
518+
void decodeValue(uint8_t* data, bool lazy) override
519+
{
520+
m_Value = std::string(reinterpret_cast<char*>(data), m_ValueLength);
521+
}
522+
std::vector<uint8_t> encodeValue() const override
523+
{
524+
return { m_Value.begin(), m_Value.end() };
525+
}
526+
527+
std::vector<std::string> toStringList() override
528+
{
529+
return { Asn1Record::toStringList().front() + ", Value: " + getValue() };
530+
}
531+
532+
std::string m_Value;
533+
};
534+
495535
/// @class Asn1OctetStringRecord
496536
/// Represents an ASN.1 record with a value of type Octet String
497-
class Asn1OctetStringRecord : public Asn1PrimitiveRecord
537+
class Asn1OctetStringRecord : public Asn1StringRecord<Asn1UniversalTagType::OctetString>
498538
{
499539
friend class Asn1Record;
500540

501541
public:
502-
/// A constructor to create a record of type Octet String from a printable value
503-
/// @param value A string to set as the record value
504-
explicit Asn1OctetStringRecord(const std::string& value);
542+
using Asn1StringRecord::Asn1StringRecord;
505543

506544
/// A constructor to create a record of type Octet String from a non-printable value
507545
/// @param value A byte array to set as the record value
508546
/// @param valueLength The length of the byte array
509547
explicit Asn1OctetStringRecord(const uint8_t* value, size_t valueLength);
510548

511-
/// @return The string value of this record
512-
std::string getValue()
513-
{
514-
decodeValueIfNeeded();
515-
return m_Value;
516-
};
549+
/// A constructor to create a record from a printable string value
550+
/// @param value A string to set as the record value
551+
explicit Asn1OctetStringRecord(const std::string& value) : Asn1StringRecord(value)
552+
{}
517553

518554
protected:
519555
void decodeValue(uint8_t* data, bool lazy) override;
520556
std::vector<uint8_t> encodeValue() const override;
521557

522-
std::vector<std::string> toStringList() override;
523-
524558
private:
525-
std::string m_Value;
559+
Asn1OctetStringRecord() = default;
560+
526561
bool m_IsPrintable = true;
562+
};
527563

528-
Asn1OctetStringRecord() = default;
564+
/// @class Asn1UTF8StringRecord
565+
/// Represents an ASN.1 record with a value of type UTF8 String
566+
class Asn1UTF8StringRecord : public Asn1StringRecord<Asn1UniversalTagType::UTF8String>
567+
{
568+
friend class Asn1Record;
569+
570+
public:
571+
/// A constructor to create a record from a printable string value
572+
/// @param value A string to set as the record value
573+
explicit Asn1UTF8StringRecord(const std::string& value) : Asn1StringRecord(value)
574+
{}
575+
576+
private:
577+
Asn1UTF8StringRecord() = default;
578+
};
579+
580+
/// @class Asn1PrintableStringRecord
581+
/// Represents an ASN.1 record with a value of type Printable String
582+
class Asn1PrintableStringRecord : public Asn1StringRecord<Asn1UniversalTagType::PrintableString>
583+
{
584+
friend class Asn1Record;
585+
586+
public:
587+
/// A constructor to create a record from a printable string value
588+
/// @param value A string to set as the record value
589+
explicit Asn1PrintableStringRecord(const std::string& value) : Asn1StringRecord(value)
590+
{}
591+
592+
private:
593+
Asn1PrintableStringRecord() = default;
594+
};
595+
596+
/// @class Asn1IA5StringRecord
597+
/// Represents an ASN.1 record with a value of type IA5 String
598+
class Asn1IA5StringRecord : public Asn1StringRecord<Asn1UniversalTagType::IA5String>
599+
{
600+
friend class Asn1Record;
601+
602+
public:
603+
/// A constructor to create a record from a printable string value
604+
/// @param value A string to set as the record value
605+
explicit Asn1IA5StringRecord(const std::string& value) : Asn1StringRecord(value)
606+
{}
607+
608+
private:
609+
Asn1IA5StringRecord() = default;
529610
};
530611

531612
/// @class Asn1BooleanRecord

Packet++/src/Asn1Codec.cpp

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,21 @@ namespace pcpp
333333
newRecord = new Asn1OctetStringRecord();
334334
break;
335335
}
336+
case Asn1UniversalTagType::UTF8String:
337+
{
338+
newRecord = new Asn1UTF8StringRecord();
339+
break;
340+
}
341+
case Asn1UniversalTagType::PrintableString:
342+
{
343+
newRecord = new Asn1PrintableStringRecord();
344+
break;
345+
}
346+
case Asn1UniversalTagType::IA5String:
347+
{
348+
newRecord = new Asn1IA5StringRecord();
349+
break;
350+
}
336351
case Asn1UniversalTagType::Boolean:
337352
{
338353
newRecord = new Asn1BooleanRecord();
@@ -709,22 +724,11 @@ namespace pcpp
709724
m_TagType = static_cast<uint8_t>(Asn1UniversalTagType::Enumerated);
710725
}
711726

712-
Asn1OctetStringRecord::Asn1OctetStringRecord(const std::string& value)
713-
: Asn1PrimitiveRecord(Asn1UniversalTagType::OctetString)
714-
{
715-
m_Value = value;
716-
m_ValueLength = value.size();
717-
m_TotalLength = m_ValueLength + 2;
718-
m_IsPrintable = true;
719-
}
720-
721-
Asn1OctetStringRecord::Asn1OctetStringRecord(const uint8_t* value, size_t valueLength)
722-
: Asn1PrimitiveRecord(Asn1UniversalTagType::OctetString)
727+
Asn1OctetStringRecord::Asn1OctetStringRecord(const uint8_t* value, size_t valueLength) : m_IsPrintable(false)
723728
{
724729
m_Value = byteArrayToHexString(value, valueLength);
725730
m_ValueLength = valueLength;
726731
m_TotalLength = m_ValueLength + 2;
727-
m_IsPrintable = false;
728732
}
729733

730734
void Asn1OctetStringRecord::decodeValue(uint8_t* data, bool lazy)
@@ -735,7 +739,7 @@ namespace pcpp
735739

736740
if (m_IsPrintable)
737741
{
738-
m_Value = std::string(value, m_ValueLength);
742+
Asn1StringRecord::decodeValue(data, lazy);
739743
}
740744
else
741745
{
@@ -747,7 +751,7 @@ namespace pcpp
747751
{
748752
if (m_IsPrintable)
749753
{
750-
return { m_Value.begin(), m_Value.end() };
754+
return Asn1StringRecord::encodeValue();
751755
}
752756

753757
// converting the hex stream to a byte array.
@@ -760,11 +764,6 @@ namespace pcpp
760764
return rawValue;
761765
}
762766

763-
std::vector<std::string> Asn1OctetStringRecord::toStringList()
764-
{
765-
return { Asn1Record::toStringList().front() + ", Value: " + getValue() };
766-
}
767-
768767
Asn1BooleanRecord::Asn1BooleanRecord(bool value) : Asn1PrimitiveRecord(Asn1UniversalTagType::Boolean)
769768
{
770769
m_Value = value;

Tests/Packet++Test/Tests/Asn1Tests.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,51 @@ PTF_TEST_CASE(Asn1DecodingTest)
186186
PTF_ASSERT_EQUAL(record->toString(), "OctetString, Length: 2+8, Value: 3006020201f40400");
187187
}
188188

189+
// UTF8String
190+
{
191+
uint8_t data[30];
192+
auto dataLen = pcpp::hexStringToByteArray("0c1ae697a5ed959ce0b8aae0a48541cea9d0afd7a9e4bda0f09f8c8d", data, 30);
193+
auto record = pcpp::Asn1Record::decode(data, dataLen);
194+
195+
PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass);
196+
PTF_ASSERT_FALSE(record->isConstructed());
197+
PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::UTF8String, enumclass);
198+
PTF_ASSERT_EQUAL(record->getTotalLength(), 28);
199+
PTF_ASSERT_EQUAL(record->getValueLength(), 26);
200+
PTF_ASSERT_EQUAL(record->castAs<pcpp::Asn1UTF8StringRecord>()->getValue(), "日한สअAΩЯש你🌍");
201+
PTF_ASSERT_EQUAL(record->toString(), "UTF8String, Length: 2+26, Value: 日한สअAΩЯש你🌍");
202+
}
203+
204+
// PrintableString
205+
{
206+
uint8_t data[20];
207+
auto dataLen = pcpp::hexStringToByteArray("13027573", data, 20);
208+
auto record = pcpp::Asn1Record::decode(data, dataLen);
209+
210+
PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass);
211+
PTF_ASSERT_FALSE(record->isConstructed());
212+
PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::PrintableString, enumclass);
213+
PTF_ASSERT_EQUAL(record->getTotalLength(), 4);
214+
PTF_ASSERT_EQUAL(record->getValueLength(), 2);
215+
PTF_ASSERT_EQUAL(record->castAs<pcpp::Asn1PrintableStringRecord>()->getValue(), "us");
216+
PTF_ASSERT_EQUAL(record->toString(), "PrintableString, Length: 2+2, Value: us");
217+
}
218+
219+
// IA5String
220+
{
221+
uint8_t data[20];
222+
auto dataLen = pcpp::hexStringToByteArray("1609414243313233402324", data, 20);
223+
auto record = pcpp::Asn1Record::decode(data, dataLen);
224+
225+
PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass);
226+
PTF_ASSERT_FALSE(record->isConstructed());
227+
PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::IA5String, enumclass);
228+
PTF_ASSERT_EQUAL(record->getTotalLength(), 11);
229+
PTF_ASSERT_EQUAL(record->getValueLength(), 9);
230+
PTF_ASSERT_EQUAL(record->castAs<pcpp::Asn1IA5StringRecord>()->getValue(), "ABC123@#$");
231+
PTF_ASSERT_EQUAL(record->toString(), "IA5String, Length: 2+9, Value: ABC123@#$");
232+
}
233+
189234
// Null
190235
{
191236
uint8_t data[20];
@@ -810,6 +855,63 @@ PTF_TEST_CASE(Asn1EncodingTest)
810855
PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen)
811856
}
812857

858+
// UTF8String
859+
{
860+
pcpp::Asn1UTF8StringRecord record("日한สअAΩЯש你🌍");
861+
862+
PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass);
863+
PTF_ASSERT_FALSE(record.isConstructed());
864+
PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::UTF8String, enumclass);
865+
PTF_ASSERT_EQUAL(record.getTotalLength(), 28);
866+
PTF_ASSERT_EQUAL(record.getValueLength(), 26);
867+
PTF_ASSERT_EQUAL(record.getValue(), "日한สअAΩЯש你🌍");
868+
869+
uint8_t data[30];
870+
auto dataLen = pcpp::hexStringToByteArray("0c1ae697a5ed959ce0b8aae0a48541cea9d0afd7a9e4bda0f09f8c8d", data, 30);
871+
872+
auto encodedValue = record.encode();
873+
PTF_ASSERT_EQUAL(encodedValue.size(), dataLen);
874+
PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen)
875+
}
876+
877+
// PrintableString
878+
{
879+
pcpp::Asn1PrintableStringRecord record("us");
880+
881+
PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass);
882+
PTF_ASSERT_FALSE(record.isConstructed());
883+
PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::PrintableString, enumclass);
884+
PTF_ASSERT_EQUAL(record.getTotalLength(), 4);
885+
PTF_ASSERT_EQUAL(record.getValueLength(), 2);
886+
PTF_ASSERT_EQUAL(record.getValue(), "us");
887+
888+
uint8_t data[20];
889+
auto dataLen = pcpp::hexStringToByteArray("13027573", data, 20);
890+
891+
auto encodedValue = record.encode();
892+
PTF_ASSERT_EQUAL(encodedValue.size(), dataLen);
893+
PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen)
894+
}
895+
896+
// IA5String
897+
{
898+
pcpp::Asn1IA5StringRecord record("ABC123@#$");
899+
900+
PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass);
901+
PTF_ASSERT_FALSE(record.isConstructed());
902+
PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::IA5String, enumclass);
903+
PTF_ASSERT_EQUAL(record.getTotalLength(), 11);
904+
PTF_ASSERT_EQUAL(record.getValueLength(), 9);
905+
PTF_ASSERT_EQUAL(record.getValue(), "ABC123@#$");
906+
907+
uint8_t data[20];
908+
auto dataLen = pcpp::hexStringToByteArray("1609414243313233402324", data, 20);
909+
910+
auto encodedValue = record.encode();
911+
PTF_ASSERT_EQUAL(encodedValue.size(), dataLen);
912+
PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen)
913+
}
914+
813915
// Boolean - true
814916
{
815917
pcpp::Asn1BooleanRecord record(true);

0 commit comments

Comments
 (0)