22using System . Diagnostics ;
33using System . Formats . Asn1 ;
44using System . Net ;
5+ using System . Runtime . CompilerServices ;
56using System . Security . Cryptography ;
67using System . Security . Cryptography . X509Certificates ;
78using Coder . Desktop . Vpn . Utilities ;
@@ -109,9 +110,11 @@ private static bool IsExtendedValidationCertificate(X509Certificate2 cert)
109110
110111 // RFC 5280 4.2: "A certificate MUST NOT include more than one instance
111112 // of a particular extension."
112- var certificatePoliciesExt = cert . Extensions . FirstOrDefault ( e => e . Oid ? . Value == CertificatePoliciesOid . Value ) ;
113- if ( certificatePoliciesExt == null )
113+ var policyExtensions = cert . Extensions . Where ( e => e . Oid ? . Value == CertificatePoliciesOid . Value ) . ToList ( ) ;
114+ if ( policyExtensions . Count == 0 )
114115 return false ;
116+ Assert ( policyExtensions . Count == 1 , "certificate contains more than one CertificatePolicies extension" ) ;
117+ var certificatePoliciesExt = policyExtensions [ 0 ] ;
115118
116119 // RFC 5280 4.2.1.4
117120 // certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
@@ -122,25 +125,36 @@ private static bool IsExtendedValidationCertificate(X509Certificate2 cert)
122125 // }
123126 try
124127 {
125- AsnDecoder . ReadSequence ( certificatePoliciesExt . RawData , AsnEncodingRules . DER , out var contentOffset ,
126- out _ , out var bytesConsumed ) ;
127- if ( bytesConsumed != certificatePoliciesExt . RawData . Length )
128- throw new Exception (
129- $ "Parsed Certificate Policies sequence length is incorrect: Consumed={ bytesConsumed } , Expected={ certificatePoliciesExt . RawData . Length } ") ;
128+ AsnDecoder . ReadSequence ( certificatePoliciesExt . RawData , AsnEncodingRules . DER , out var originalContentOffset ,
129+ out var contentLength , out var bytesConsumed ) ;
130+ Assert ( bytesConsumed == certificatePoliciesExt . RawData . Length , "incorrect outer sequence length" ) ;
131+ Assert ( originalContentOffset >= 0 , "invalid outer sequence content offset" ) ;
132+ Assert ( contentLength > 0 , "invalid outer sequence content length" ) ;
133+
134+ var contentOffset = originalContentOffset ;
135+ var endOffset = originalContentOffset + contentLength ;
136+ Assert ( endOffset <= certificatePoliciesExt . RawData . Length , "invalid outer sequence end offset" ) ;
130137
131138 // For each policy...
132- while ( contentOffset < certificatePoliciesExt . RawData . Length )
139+ while ( contentOffset < endOffset )
133140 {
134141 // Parse a sequence from [contentOffset:].
135- var slice = certificatePoliciesExt . RawData . AsSpan ( contentOffset ) ;
142+ var slice = certificatePoliciesExt . RawData . AsSpan ( contentOffset , endOffset - contentOffset ) ;
136143 AsnDecoder . ReadSequence ( slice , AsnEncodingRules . DER , out var innerContentOffset ,
137144 out var innerContentLength , out var innerBytesConsumed ) ;
145+ Assert ( innerBytesConsumed <= slice . Length , "incorrect inner sequence length" ) ;
146+ Assert ( innerContentOffset >= 0 , "invalid inner sequence content offset" ) ;
147+ Assert ( innerContentLength > 0 , "invalid inner sequence content length" ) ;
148+ Assert ( innerContentOffset + innerContentLength <= slice . Length , "invalid inner sequence end offset" ) ;
149+
138150 // Advance the outer offset by the consumed bytes.
139151 contentOffset += innerBytesConsumed ;
140152
141153 // Parse the first value in the sequence as an Oid.
142154 slice = slice . Slice ( innerContentOffset , innerContentLength ) ;
143- var oid = AsnDecoder . ReadObjectIdentifier ( slice , AsnEncodingRules . DER , out _ ) ;
155+ var oid = AsnDecoder . ReadObjectIdentifier ( slice , AsnEncodingRules . DER , out var oidBytesConsumed ) ;
156+ Assert ( oidBytesConsumed > 0 , "invalid inner sequence OID length" ) ;
157+ Assert ( oidBytesConsumed <= slice . Length , "invalid inner sequence OID length" ) ;
144158 if ( oid == ExtendedValidationCodeSigningOid . Value )
145159 return true ;
146160
@@ -157,6 +171,13 @@ private static bool IsExtendedValidationCertificate(X509Certificate2 cert)
157171
158172 return false ;
159173 }
174+
175+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
176+ private static void Assert ( bool condition , string message )
177+ {
178+ if ( ! condition )
179+ throw new Exception ( "Failed certificate parse assertion: " + message ) ;
180+ }
160181}
161182
162183public class AssemblyVersionDownloadValidator : IDownloadValidator
0 commit comments