@@ -341,6 +341,11 @@ namespace pcpp
341341 newRecord = new Asn1NullRecord ();
342342 break ;
343343 }
344+ case Asn1UniversalTagType::ObjectIdentifier:
345+ {
346+ newRecord = new Asn1ObjectIdentifierRecord ();
347+ break ;
348+ }
344349 default :
345350 {
346351 newRecord = new Asn1GenericRecord ();
@@ -771,4 +776,186 @@ namespace pcpp
771776 m_ValueLength = 0 ;
772777 m_TotalLength = 2 ;
773778 }
779+
780+ Asn1ObjectIdentifier::Asn1ObjectIdentifier (const uint8_t * data, size_t dataLen)
781+ {
782+ // A description of OID encoding can be found here:
783+ // https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-object-identifier?redirectedfrom=MSDN
784+
785+ if (!data || dataLen == 0 )
786+ {
787+ throw std::invalid_argument (" Malformed OID: Not enough bytes for the first component" );
788+ }
789+
790+ size_t currentByteIndex = 0 ;
791+ std::vector<uint32_t > components;
792+
793+ uint8_t firstByte = data[currentByteIndex++];
794+ // Decode the first byte: first_component * 40 + second_component
795+ components.push_back (static_cast <uint32_t >(firstByte / 40 ));
796+ components.push_back (static_cast <uint32_t >(firstByte % 40 ));
797+
798+ uint32_t currentComponentValue = 0 ;
799+ bool componentStarted = false ;
800+
801+ // Process remaining bytes using base-128 encoding
802+ while (currentByteIndex < dataLen)
803+ {
804+ uint8_t byte = data[currentByteIndex++];
805+
806+ // Shift previous bits left by 7 and append lower 7 bits
807+ currentComponentValue = (currentComponentValue << 7 ) | (byte & 0x7f );
808+ componentStarted = true ;
809+
810+ // If the MSB is 0, this is the final byte of the current value
811+ if ((byte & 0x80 ) == 0 )
812+ {
813+ components.push_back (currentComponentValue);
814+ currentComponentValue = 0 ;
815+ componentStarted = false ;
816+ }
817+ }
818+
819+ if (componentStarted)
820+ {
821+ throw std::invalid_argument (" Malformed OID: Incomplete component at end of data" );
822+ }
823+
824+ m_Components = components;
825+ }
826+
827+ Asn1ObjectIdentifier::Asn1ObjectIdentifier (const std::string& oidString)
828+ {
829+ std::vector<uint32_t > components;
830+ std::istringstream stream (oidString);
831+ std::string token;
832+
833+ while (std::getline (stream, token, ' .' ))
834+ {
835+ if (token.empty ())
836+ {
837+ throw std::invalid_argument (" Malformed OID: empty component" );
838+ }
839+
840+ unsigned long long value;
841+ try
842+ {
843+ value = std::stoull (token);
844+ }
845+ catch (const std::exception&)
846+ {
847+ throw std::invalid_argument (" Malformed OID: invalid component" );
848+ }
849+
850+ if (value > std::numeric_limits<uint32_t >::max ())
851+ {
852+ throw std::invalid_argument (" Malformed OID: component out of uint32_t range" );
853+ }
854+
855+ components.push_back (static_cast <uint32_t >(value));
856+ }
857+
858+ if (components.size () < 2 )
859+ {
860+ throw std::invalid_argument (" Malformed OID: an OID must have at least two components" );
861+ }
862+
863+ if (components[0 ] > 2 )
864+ {
865+ throw std::invalid_argument (" Malformed OID: first component must be 0, 1, or 2" );
866+ }
867+
868+ if ((components[0 ] == 0 || components[0 ] == 1 ) && components[1 ] >= 40 )
869+ {
870+ throw std::invalid_argument (
871+ " Malformed OID: second component must be less than 40 when first component is 0 or 1" );
872+ }
873+
874+ m_Components = components;
875+ }
876+
877+ std::string Asn1ObjectIdentifier::toString () const
878+ {
879+ if (m_Components.empty ())
880+ {
881+ return " " ;
882+ }
883+
884+ std::ostringstream stream;
885+ stream << m_Components[0 ];
886+
887+ for (size_t i = 1 ; i < m_Components.size (); ++i)
888+ {
889+ stream << " ." << m_Components[i];
890+ }
891+ return stream.str ();
892+ }
893+
894+ std::vector<uint8_t > Asn1ObjectIdentifier::toBytes () const
895+ {
896+ // A description of OID encoding can be found here:
897+ // https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-object-identifier?redirectedfrom=MSDN
898+
899+ if (m_Components.size () < 2 )
900+ {
901+ throw std::runtime_error (" OID must have at least two components to encode." );
902+ }
903+
904+ std::vector<uint8_t > encoded;
905+
906+ // Encode the first two components into one byte
907+ uint32_t firstComponent = m_Components[0 ];
908+ uint32_t secondComponent = m_Components[1 ];
909+ encoded.push_back (static_cast <uint8_t >(firstComponent * 40 + secondComponent));
910+
911+ // Encode remaining components using base-128 encoding
912+ for (size_t i = 2 ; i < m_Components.size (); ++i)
913+ {
914+ uint32_t currentComponent = m_Components[i];
915+ std::vector<uint8_t > temp;
916+
917+ // At least one byte must be generated even if value is 0
918+ do
919+ {
920+ temp.push_back (static_cast <uint8_t >(currentComponent & 0x7F ));
921+ currentComponent >>= 7 ;
922+ } while (currentComponent > 0 );
923+
924+ // Set continuation bits (MSB) for all but the last byte
925+ for (size_t j = temp.size (); j-- > 0 ;)
926+ {
927+ uint8_t byte = temp[j];
928+ if (j != 0 )
929+ {
930+ byte |= 0x80 ;
931+ }
932+ encoded.push_back (byte);
933+ }
934+ }
935+
936+ return encoded;
937+ }
938+
939+ Asn1ObjectIdentifierRecord::Asn1ObjectIdentifierRecord (const Asn1ObjectIdentifier& value)
940+ : Asn1PrimitiveRecord(Asn1UniversalTagType::ObjectIdentifier)
941+ {
942+ m_Value = value;
943+ m_ValueLength = value.toBytes ().size ();
944+ m_TotalLength = m_ValueLength + 2 ;
945+ }
946+
947+ void Asn1ObjectIdentifierRecord::decodeValue (uint8_t * data, bool lazy)
948+ {
949+ m_Value = Asn1ObjectIdentifier (data, m_ValueLength);
950+ }
951+
952+ std::vector<uint8_t > Asn1ObjectIdentifierRecord::encodeValue () const
953+ {
954+ return m_Value.toBytes ();
955+ }
956+
957+ std::vector<std::string> Asn1ObjectIdentifierRecord::toStringList ()
958+ {
959+ return { Asn1Record::toStringList ().front () + " , Value: " + getValue ().toString () };
960+ }
774961} // namespace pcpp
0 commit comments