@@ -341,6 +341,11 @@ namespace pcpp
341
341
newRecord = new Asn1NullRecord ();
342
342
break ;
343
343
}
344
+ case Asn1UniversalTagType::ObjectIdentifier:
345
+ {
346
+ newRecord = new Asn1ObjectIdentifierRecord ();
347
+ break ;
348
+ }
344
349
default :
345
350
{
346
351
newRecord = new Asn1GenericRecord ();
@@ -771,4 +776,186 @@ namespace pcpp
771
776
m_ValueLength = 0 ;
772
777
m_TotalLength = 2 ;
773
778
}
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
+ }
774
961
} // namespace pcpp
0 commit comments