Skip to content

Commit 0588d88

Browse files
authored
Add decoding and encoding of BitString ASN.1 records (#1850)
1 parent d66f0d4 commit 0588d88

File tree

4 files changed

+227
-0
lines changed

4 files changed

+227
-0
lines changed

Packet++/header/Asn1Codec.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <stdexcept>
77
#include <sstream>
88
#include <chrono>
9+
#include <bitset>
910
#include "PointerVector.h"
1011

1112
/// @file
@@ -747,4 +748,59 @@ namespace pcpp
747748
Asn1GeneralizedTimeRecord() = default;
748749
std::string m_Timezone;
749750
};
751+
752+
/// @class Asn1BitStringRecord
753+
/// Represents an ASN.1 record with a value of type BitString
754+
class Asn1BitStringRecord : public Asn1PrimitiveRecord
755+
{
756+
friend class Asn1Record;
757+
758+
public:
759+
/// A constructor to create a record of type BitString
760+
/// @param value A bit string to set as the record value
761+
/// @throw std::invalid_argument if the string is not a valid bit string
762+
explicit Asn1BitStringRecord(const std::string& value);
763+
764+
/// @return The bit string value of this record
765+
std::string getValue()
766+
{
767+
decodeValueIfNeeded();
768+
return m_Value.toString();
769+
};
770+
771+
protected:
772+
void decodeValue(uint8_t* data, bool lazy) override;
773+
std::vector<uint8_t> encodeValue() const override;
774+
775+
std::vector<std::string> toStringList() override;
776+
777+
private:
778+
class BitSet
779+
{
780+
public:
781+
BitSet() = default;
782+
explicit BitSet(const std::string& value);
783+
BitSet(const uint8_t* data, size_t numBits);
784+
785+
BitSet& operator=(const std::string& value);
786+
787+
size_t sizeInBytes() const;
788+
std::string toString() const;
789+
std::vector<uint8_t> toBytes() const;
790+
size_t getNumBits() const
791+
{
792+
return m_NumBits;
793+
}
794+
795+
private:
796+
void initFromString(const std::string& value);
797+
798+
std::vector<std::bitset<8>> m_Data;
799+
size_t m_NumBits = 0;
800+
};
801+
802+
Asn1BitStringRecord() = default;
803+
804+
BitSet m_Value;
805+
};
750806
} // namespace pcpp

Packet++/src/Asn1Codec.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,11 @@ namespace pcpp
338338
newRecord = new Asn1BooleanRecord();
339339
break;
340340
}
341+
case Asn1UniversalTagType::BitString:
342+
{
343+
newRecord = new Asn1BitStringRecord();
344+
break;
345+
}
341346
case Asn1UniversalTagType::Null:
342347
{
343348
newRecord = new Asn1NullRecord();
@@ -1203,4 +1208,110 @@ namespace pcpp
12031208
auto timeString = osstream.str();
12041209
return { timeString.begin(), timeString.end() };
12051210
}
1211+
1212+
void Asn1BitStringRecord::BitSet::initFromString(const std::string& value)
1213+
{
1214+
m_NumBits = value.length();
1215+
1216+
size_t numBytes = (m_NumBits + 7) / 8;
1217+
m_Data.clear();
1218+
m_Data.reserve(numBytes);
1219+
1220+
size_t i = 0;
1221+
while (i < value.length())
1222+
{
1223+
std::string curByteString = value.substr(i, 8);
1224+
curByteString.append(8 - curByteString.length(), '0');
1225+
try
1226+
{
1227+
std::bitset<8> bs(curByteString);
1228+
m_Data.push_back(bs);
1229+
i += 8;
1230+
}
1231+
catch (const std::invalid_argument&)
1232+
{
1233+
throw std::invalid_argument("Invalid bit string");
1234+
}
1235+
}
1236+
}
1237+
1238+
Asn1BitStringRecord::BitSet::BitSet(const std::string& value)
1239+
{
1240+
initFromString(value);
1241+
}
1242+
1243+
Asn1BitStringRecord::BitSet::BitSet(const uint8_t* data, size_t numBits) : m_NumBits(numBits)
1244+
{
1245+
if (!data || !numBits)
1246+
{
1247+
throw std::invalid_argument("Provided data is null or num of bits is 0");
1248+
}
1249+
1250+
size_t requiredBytes = (m_NumBits + 7) / 8;
1251+
m_Data.resize(requiredBytes);
1252+
std::copy_n(data, requiredBytes, m_Data.begin());
1253+
}
1254+
1255+
Asn1BitStringRecord::BitSet& Asn1BitStringRecord::BitSet::operator=(const std::string& value)
1256+
{
1257+
initFromString(value);
1258+
return *this;
1259+
}
1260+
1261+
std::string Asn1BitStringRecord::BitSet::toString() const
1262+
{
1263+
std::string result;
1264+
result.reserve(m_Data.size() * 8);
1265+
for (const auto bs : m_Data)
1266+
{
1267+
result += bs.to_string();
1268+
}
1269+
result.resize(m_NumBits);
1270+
return result;
1271+
}
1272+
1273+
std::vector<uint8_t> Asn1BitStringRecord::BitSet::toBytes() const
1274+
{
1275+
std::vector<uint8_t> result;
1276+
result.reserve(m_Data.size());
1277+
for (const auto& bs : m_Data)
1278+
{
1279+
result.push_back(static_cast<uint8_t>(bs.to_ulong()));
1280+
}
1281+
1282+
return result;
1283+
}
1284+
1285+
size_t Asn1BitStringRecord::BitSet::sizeInBytes() const
1286+
{
1287+
return m_Data.size();
1288+
}
1289+
1290+
Asn1BitStringRecord::Asn1BitStringRecord(const std::string& value)
1291+
: Asn1PrimitiveRecord(Asn1UniversalTagType::BitString)
1292+
{
1293+
m_Value = value;
1294+
m_ValueLength = m_Value.sizeInBytes() + 1;
1295+
m_TotalLength = m_ValueLength + 2;
1296+
}
1297+
1298+
void Asn1BitStringRecord::decodeValue(uint8_t* data, bool lazy)
1299+
{
1300+
auto numBits = (m_ValueLength - 1) * 8 - static_cast<size_t>(data[0]);
1301+
m_Value = BitSet(data + 1, numBits);
1302+
}
1303+
1304+
std::vector<uint8_t> Asn1BitStringRecord::encodeValue() const
1305+
{
1306+
auto result = m_Value.toBytes();
1307+
size_t unusedBits = m_Value.sizeInBytes() * 8 - m_Value.getNumBits();
1308+
result.insert(result.begin(), static_cast<uint8_t>(unusedBits));
1309+
return result;
1310+
}
1311+
1312+
std::vector<std::string> Asn1BitStringRecord::toStringList()
1313+
{
1314+
return { Asn1Record::toStringList().front() + ", Value: " + m_Value.toString() };
1315+
}
1316+
12061317
} // namespace pcpp

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

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,36 @@ PTF_TEST_CASE(Asn1DecodingTest)
372372
}
373373
}
374374

375+
// BitString with 0 unused bytes
376+
{
377+
uint8_t data[20];
378+
auto dataLen = pcpp::hexStringToByteArray("030300a3b5", data, 20);
379+
auto record = pcpp::Asn1Record::decode(data, dataLen);
380+
381+
PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass);
382+
PTF_ASSERT_FALSE(record->isConstructed());
383+
PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::BitString, enumclass);
384+
PTF_ASSERT_EQUAL(record->getTotalLength(), 5);
385+
PTF_ASSERT_EQUAL(record->getValueLength(), 3);
386+
PTF_ASSERT_EQUAL(record->castAs<pcpp::Asn1BitStringRecord>()->getValue(), "1010001110110101");
387+
PTF_ASSERT_EQUAL(record->toString(), "BitString, Length: 2+3, Value: 1010001110110101");
388+
}
389+
390+
// BitString with unused bytes
391+
{
392+
uint8_t data[20];
393+
auto dataLen = pcpp::hexStringToByteArray("030306b2c0", data, 20);
394+
auto record = pcpp::Asn1Record::decode(data, dataLen);
395+
396+
PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass);
397+
PTF_ASSERT_FALSE(record->isConstructed());
398+
PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::BitString, enumclass);
399+
PTF_ASSERT_EQUAL(record->getTotalLength(), 5);
400+
PTF_ASSERT_EQUAL(record->getValueLength(), 3);
401+
PTF_ASSERT_EQUAL(record->castAs<pcpp::Asn1BitStringRecord>()->getValue(), "1011001011");
402+
PTF_ASSERT_EQUAL(record->toString(), "BitString, Length: 2+3, Value: 1011001011");
403+
}
404+
375405
// Sequence
376406
{
377407
uint8_t data[20];
@@ -979,6 +1009,35 @@ PTF_TEST_CASE(Asn1EncodingTest)
9791009
"Invalid timezone format. Use 'Z' or '+/-HHMM'.");
9801010
}
9811011

1012+
// BitString with 0 unused bytes
1013+
{
1014+
pcpp::Asn1BitStringRecord record("1010001110110101");
1015+
1016+
uint8_t data[20];
1017+
auto dataLen = pcpp::hexStringToByteArray("030300a3b5", data, 20);
1018+
1019+
auto encodedValue = record.encode();
1020+
PTF_ASSERT_EQUAL(encodedValue.size(), dataLen);
1021+
PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen)
1022+
}
1023+
1024+
// BitString with unused bytes
1025+
{
1026+
pcpp::Asn1BitStringRecord record("1011001011");
1027+
1028+
uint8_t data[20];
1029+
auto dataLen = pcpp::hexStringToByteArray("030306b2c0", data, 20);
1030+
1031+
auto encodedValue = record.encode();
1032+
PTF_ASSERT_EQUAL(encodedValue.size(), dataLen);
1033+
PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen)
1034+
}
1035+
1036+
// BitString invalid value
1037+
{
1038+
PTF_ASSERT_RAISES(pcpp::Asn1BitStringRecord record("0011invalid"), std::invalid_argument, "Invalid bit string");
1039+
}
1040+
9821041
// Sequence
9831042
{
9841043
pcpp::Asn1OctetStringRecord octestStringRecord("abcd");

cppcheckSuppressions.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ unusedStructMember:*
99

1010
noExplicitConstructor:Common++/header/IpAddress.h
1111
noExplicitConstructor:Common++/header/MacAddress.h
12+
useStlAlgorithm:Packet++/src/Asn1Codec.cpp
1213
noExplicitConstructor:Pcap++/header/PcapFileDevice.h
1314

1415
missingOverride:Pcap++/*

0 commit comments

Comments
 (0)