11// Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
4- using System . Reflection ;
54using Test . Cryptography ;
65using Xunit ;
76
87namespace System . Formats . Asn1 . Tests . Reader
98{
109 public sealed class ReadLength
1110 {
12- private delegate Asn1Tag ReadTagAndLengthDelegate (
11+ private static Asn1Tag ReadTagAndLength (
1312 ReadOnlySpan < byte > source ,
1413 AsnEncodingRules ruleSet ,
1514 out int ? parsedLength ,
16- out int bytesRead ) ;
15+ out int bytesRead )
16+ {
17+ Asn1Tag tag = Asn1Tag . Decode ( source , out int tagLength ) ;
18+ parsedLength = AsnDecoder . DecodeLength ( source . Slice ( tagLength ) , ruleSet , out int lengthLength ) ;
19+ bytesRead = tagLength + lengthLength ;
20+ return tag ;
21+ }
22+
23+ private static bool TryReadTagAndLength (
24+ ReadOnlySpan < byte > source ,
25+ AsnEncodingRules ruleSet ,
26+ out Asn1Tag tag ,
27+ out int ? parsedLength ,
28+ out int bytesRead )
29+ {
30+ Asn1Tag localTag = Asn1Tag . Decode ( source , out int tagLength ) ;
31+
32+ bool read = AsnDecoder . TryDecodeLength (
33+ source . Slice ( tagLength ) ,
34+ ruleSet ,
35+ out parsedLength ,
36+ out int lengthLength ) ;
37+
38+ if ( read )
39+ {
40+ tag = localTag ;
41+ bytesRead = tagLength + lengthLength ;
42+ }
43+ else
44+ {
45+ tag = default ;
46+ bytesRead = default ;
47+ }
1748
18- private static ReadTagAndLengthDelegate ReadTagAndLength = ( ReadTagAndLengthDelegate )
19- typeof ( AsnDecoder ) . GetMethod ( "ReadTagAndLength" , BindingFlags . Static | BindingFlags . NonPublic )
20- . CreateDelegate ( typeof ( ReadTagAndLengthDelegate ) ) ;
49+ return read ;
50+ }
2151
2252 [ Theory ]
2353 [ InlineData ( 4 , 0 , "0400" ) ]
@@ -39,6 +69,13 @@ public static void MinimalPrimitiveLength(int tagValue, int length, string input
3969 Assert . False ( tag . IsConstructed , "tag.IsConstructed" ) ;
4070 Assert . Equal ( tagValue , tag . TagValue ) ;
4171 Assert . Equal ( length , parsedLength . Value ) ;
72+
73+ Assert . True ( TryReadTagAndLength ( inputBytes , rules , out tag , out parsedLength , out bytesRead ) ) ;
74+
75+ Assert . Equal ( inputBytes . Length , bytesRead ) ;
76+ Assert . False ( tag . IsConstructed , "tag.IsConstructed" ) ;
77+ Assert . Equal ( tagValue , tag . TagValue ) ;
78+ Assert . Equal ( length , parsedLength . Value ) ;
4279 }
4380 }
4481
@@ -51,10 +88,64 @@ public static void ReadWithUnknownRuleSet(int invalidRuleSetValue)
5188
5289 Assert . Throws < ArgumentOutOfRangeException > (
5390 ( ) => new AsnReader ( data , ( AsnEncodingRules ) invalidRuleSetValue ) ) ;
91+
92+ Assert . Throws < ArgumentOutOfRangeException > (
93+ ( ) => ReadTagAndLength ( data , ( AsnEncodingRules ) invalidRuleSetValue , out _ , out _ ) ) ;
94+
95+ Assert . Throws < ArgumentOutOfRangeException > (
96+ ( ) => TryReadTagAndLength ( data , ( AsnEncodingRules ) invalidRuleSetValue , out _ , out _ , out _ ) ) ;
97+ }
98+
99+ private static void ReadValid (
100+ ReadOnlySpan < byte > source ,
101+ AsnEncodingRules ruleSet ,
102+ int ? expectedLength ,
103+ int expectedBytesRead = - 1 )
104+ {
105+ if ( expectedBytesRead < 0 )
106+ {
107+ expectedBytesRead = source . Length ;
108+ }
109+
110+ ReadTagAndLength (
111+ source ,
112+ ruleSet ,
113+ out int ? length ,
114+ out int bytesRead ) ;
115+
116+ Assert . Equal ( expectedBytesRead , bytesRead ) ;
117+ Assert . Equal ( expectedLength , length ) ;
118+
119+ bool read = TryReadTagAndLength (
120+ source ,
121+ ruleSet ,
122+ out _ ,
123+ out length ,
124+ out bytesRead ) ;
125+
126+ Assert . True ( read ) ;
127+ Assert . Equal ( expectedBytesRead , bytesRead ) ;
128+ Assert . Equal ( expectedLength , length ) ;
129+ }
130+
131+ private static void ReadInvalid ( byte [ ] source , AsnEncodingRules ruleSet )
132+ {
133+ Assert . Throws < AsnContentException > (
134+ ( ) => ReadTagAndLength ( source , ruleSet , out _ , out _ ) ) ;
135+
136+ Asn1Tag tag ;
137+ int ? decodedLength ;
138+ int bytesConsumed ;
139+
140+ Assert . False (
141+ TryReadTagAndLength ( source , ruleSet , out tag , out decodedLength , out bytesConsumed ) ) ;
142+
143+ Assert . True ( tag == default ) ;
144+ Assert . Null ( decodedLength ) ;
145+ Assert . Equal ( 0 , bytesConsumed ) ;
54146 }
55147
56148 [ Theory ]
57- [ InlineData ( "" ) ]
58149 [ InlineData ( "05" ) ]
59150 [ InlineData ( "0481" ) ]
60151 [ InlineData ( "048201" ) ]
@@ -64,18 +155,16 @@ public static void ReadWithInsufficientData(string inputHex)
64155 {
65156 byte [ ] inputData = inputHex . HexToByteArray ( ) ;
66157
67- Assert . Throws < AsnContentException > (
68- ( ) => ReadTagAndLength ( inputData , AsnEncodingRules . DER , out _ , out _ ) ) ;
158+ ReadInvalid ( inputData , AsnEncodingRules . BER ) ;
159+ ReadInvalid ( inputData , AsnEncodingRules . CER ) ;
160+ ReadInvalid ( inputData , AsnEncodingRules . DER ) ;
69161 }
70162
71163 [ Theory ]
72164 [ InlineData ( "DER indefinite constructed" , AsnEncodingRules . DER , "3080" + "0500" + "0000" ) ]
73165 [ InlineData ( "0xFF-BER" , AsnEncodingRules . BER , "04FF" ) ]
74166 [ InlineData ( "0xFF-CER" , AsnEncodingRules . CER , "04FF" ) ]
75167 [ InlineData ( "0xFF-DER" , AsnEncodingRules . DER , "04FF" ) ]
76- [ InlineData ( "CER definite constructed" , AsnEncodingRules . CER , "30820500" ) ]
77- [ InlineData ( "BER indefinite primitive" , AsnEncodingRules . BER , "0480" + "0000" ) ]
78- [ InlineData ( "CER indefinite primitive" , AsnEncodingRules . CER , "0480" + "0000" ) ]
79168 [ InlineData ( "DER indefinite primitive" , AsnEncodingRules . DER , "0480" + "0000" ) ]
80169 [ InlineData ( "DER non-minimal 0" , AsnEncodingRules . DER , "048100" ) ]
81170 [ InlineData ( "DER non-minimal 7F" , AsnEncodingRules . DER , "04817F" ) ]
@@ -102,10 +191,28 @@ public static void InvalidLengths(
102191 {
103192 _ = description ;
104193 byte [ ] inputData = inputHex . HexToByteArray ( ) ;
105- AsnReader reader = new AsnReader ( inputData , rules ) ;
106194
107- Assert . Throws < AsnContentException > (
108- ( ) => ReadTagAndLength ( inputData , rules , out _ , out _ ) ) ;
195+ ReadInvalid ( inputData , rules ) ;
196+ }
197+
198+ [ Theory ]
199+ [ InlineData ( "CER definite constructed" , AsnEncodingRules . CER , 0x0500 , 4 , "30820500" ) ]
200+ [ InlineData ( "BER indefinite primitive" , AsnEncodingRules . BER , null , 2 , "0480" + "0000" ) ]
201+ [ InlineData ( "CER indefinite primitive" , AsnEncodingRules . CER , null , 2 , "0480" + "0000" ) ]
202+ public static void ContextuallyInvalidLengths (
203+ string description ,
204+ AsnEncodingRules rules ,
205+ int ? expectedLength ,
206+ int expectedBytesRead ,
207+ string inputHex )
208+ {
209+ // These inputs will all throw from AsnDecoder.ReadTagAndLength, but require
210+ // the tag as context.
211+
212+ _ = description ;
213+ byte [ ] inputData = inputHex . HexToByteArray ( ) ;
214+
215+ ReadValid ( inputData , rules , expectedLength , expectedBytesRead ) ;
109216 }
110217
111218 [ Theory ]
@@ -117,18 +224,8 @@ public static void IndefiniteLength(AsnEncodingRules ruleSet)
117224 // NULL
118225 // End-of-Contents
119226 byte [ ] data = { 0x30 , 0x80 , 0x05 , 0x00 , 0x00 , 0x00 } ;
120- AsnReader reader = new AsnReader ( data , ruleSet ) ;
121-
122- Asn1Tag tag = ReadTagAndLength (
123- data ,
124- ruleSet ,
125- out int ? length ,
126- out int bytesRead ) ;
127227
128- Assert . Equal ( 2 , bytesRead ) ;
129- Assert . False ( length . HasValue , "length.HasValue" ) ;
130- Assert . Equal ( ( int ) UniversalTagNumber . Sequence , tag . TagValue ) ;
131- Assert . True ( tag . IsConstructed , "tag.IsConstructed" ) ;
228+ ReadValid ( data , ruleSet , null , 2 ) ;
132229 }
133230
134231 [ Theory ]
@@ -138,42 +235,25 @@ public static void IndefiniteLength(AsnEncodingRules ruleSet)
138235 public static void BerNonMinimalLength ( int expectedLength , string inputHex )
139236 {
140237 byte [ ] inputData = inputHex . HexToByteArray ( ) ;
141- AsnReader reader = new AsnReader ( inputData , AsnEncodingRules . BER ) ;
142238
143- Asn1Tag tag = ReadTagAndLength (
144- inputData ,
145- AsnEncodingRules . BER ,
146- out int ? length ,
147- out int bytesRead ) ;
148-
149- Assert . Equal ( inputData . Length , bytesRead ) ;
150- Assert . Equal ( expectedLength , length . Value ) ;
151- // ReadTagAndLength doesn't move the _data span forward.
152- Assert . True ( reader . HasData , "reader.HasData" ) ;
239+ ReadValid ( inputData , AsnEncodingRules . BER , expectedLength ) ;
240+ ReadInvalid ( inputData , AsnEncodingRules . CER ) ;
241+ ReadInvalid ( inputData , AsnEncodingRules . DER ) ;
153242 }
154243
155244 [ Theory ]
156- [ InlineData ( AsnEncodingRules . BER , 4 , 0 , 5 , "0483000000" + "0500" ) ]
157- [ InlineData ( AsnEncodingRules . DER , 1 , 1 , 2 , "0101" + "FF" ) ]
158- [ InlineData ( AsnEncodingRules . CER , 0x10 , null , 2 , "3080" + "0500" + "0000" ) ]
245+ [ InlineData ( AsnEncodingRules . BER , 0 , 5 , "0483000000" + "0500" ) ]
246+ [ InlineData ( AsnEncodingRules . DER , 1 , 2 , "0101" + "FF" ) ]
247+ [ InlineData ( AsnEncodingRules . CER , null , 2 , "3080" + "0500" + "0000" ) ]
159248 public static void ReadWithDataRemaining (
160249 AsnEncodingRules ruleSet ,
161- int tagValue ,
162250 int ? expectedLength ,
163251 int expectedBytesRead ,
164252 string inputHex )
165253 {
166254 byte [ ] inputData = inputHex . HexToByteArray ( ) ;
167255
168- Asn1Tag tag = ReadTagAndLength (
169- inputData ,
170- ruleSet ,
171- out int ? length ,
172- out int bytesRead ) ;
173-
174- Assert . Equal ( expectedBytesRead , bytesRead ) ;
175- Assert . Equal ( tagValue , tag . TagValue ) ;
176- Assert . Equal ( expectedLength , length ) ;
256+ ReadValid ( inputData , ruleSet , expectedLength , expectedBytesRead ) ;
177257 }
178258 }
179259}
0 commit comments