2
2
using System . Diagnostics ;
3
3
using System . Formats . Asn1 ;
4
4
using System . Net ;
5
+ using System . Runtime . CompilerServices ;
5
6
using System . Security . Cryptography ;
6
7
using System . Security . Cryptography . X509Certificates ;
7
8
using Coder . Desktop . Vpn . Utilities ;
@@ -109,9 +110,11 @@ private static bool IsExtendedValidationCertificate(X509Certificate2 cert)
109
110
110
111
// RFC 5280 4.2: "A certificate MUST NOT include more than one instance
111
112
// 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 )
114
115
return false ;
116
+ Assert ( policyExtensions . Count == 1 , "certificate contains more than one CertificatePolicies extension" ) ;
117
+ var certificatePoliciesExt = policyExtensions [ 0 ] ;
115
118
116
119
// RFC 5280 4.2.1.4
117
120
// certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
@@ -122,25 +125,36 @@ private static bool IsExtendedValidationCertificate(X509Certificate2 cert)
122
125
// }
123
126
try
124
127
{
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" ) ;
130
137
131
138
// For each policy...
132
- while ( contentOffset < certificatePoliciesExt . RawData . Length )
139
+ while ( contentOffset < endOffset )
133
140
{
134
141
// Parse a sequence from [contentOffset:].
135
- var slice = certificatePoliciesExt . RawData . AsSpan ( contentOffset ) ;
142
+ var slice = certificatePoliciesExt . RawData . AsSpan ( contentOffset , endOffset - contentOffset ) ;
136
143
AsnDecoder . ReadSequence ( slice , AsnEncodingRules . DER , out var innerContentOffset ,
137
144
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
+
138
150
// Advance the outer offset by the consumed bytes.
139
151
contentOffset += innerBytesConsumed ;
140
152
141
153
// Parse the first value in the sequence as an Oid.
142
154
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" ) ;
144
158
if ( oid == ExtendedValidationCodeSigningOid . Value )
145
159
return true ;
146
160
@@ -157,6 +171,13 @@ private static bool IsExtendedValidationCertificate(X509Certificate2 cert)
157
171
158
172
return false ;
159
173
}
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
+ }
160
181
}
161
182
162
183
public class AssemblyVersionDownloadValidator : IDownloadValidator
0 commit comments