1
1
using System ;
2
2
using System . Collections . Generic ;
3
+ using System . Data ;
4
+ using System . IO ;
3
5
using System . Numerics ;
4
6
using System . Text ;
5
7
using SabreTools . IO . Extensions ;
@@ -14,17 +16,17 @@ public class TypeLengthValue
14
16
/// <summary>
15
17
/// The ASN.1 type
16
18
/// </summary>
17
- public ASN1Type Type { get ; }
19
+ public ASN1Type Type { get ; private set ; }
18
20
19
21
/// <summary>
20
22
/// Length of the value
21
23
/// </summary>
22
- public ulong Length { get ; }
24
+ public ulong Length { get ; private set ; }
23
25
24
26
/// <summary>
25
27
/// Generic value associated with <see cref="Type"/>
26
28
/// </summary>
27
- public object ? Value { get ; }
29
+ public object ? Value { get ; private set ; }
28
30
29
31
/// <summary>
30
32
/// Read from the source data array at an index
@@ -33,38 +35,26 @@ public class TypeLengthValue
33
35
/// <param name="index">Index within the array to read at</param>
34
36
public TypeLengthValue ( byte [ ] data , ref int index )
35
37
{
36
- // Get the type and modifiers
37
- Type = ( ASN1Type ) data [ index ++ ] ;
38
-
39
- // If we have an end indicator, we just return
40
- if ( Type == ASN1Type . V_ASN1_EOC )
41
- return ;
42
-
43
- // Get the length of the value
44
- Length = ReadLength ( data , ref index ) ;
45
-
46
- // Read the value
47
- #if NET20 || NET35
48
- if ( ( Type & ASN1Type . V_ASN1_CONSTRUCTED ) != 0 )
49
- #else
50
- if ( Type . HasFlag ( ASN1Type . V_ASN1_CONSTRUCTED ) )
51
- #endif
52
- {
53
- var valueList = new List < TypeLengthValue > ( ) ;
54
-
55
- int currentIndex = index ;
56
- while ( index < currentIndex + ( int ) Length )
57
- {
58
- valueList . Add ( new TypeLengthValue ( data , ref index ) ) ;
59
- }
38
+ // If the data is invalid
39
+ if ( data . Length == 0 )
40
+ throw new InvalidDataException ( nameof ( data ) ) ;
41
+ if ( index < 0 || index >= data . Length )
42
+ throw new IndexOutOfRangeException ( nameof ( index ) ) ;
43
+
44
+ using var stream = new MemoryStream ( data ) ;
45
+ stream . Seek ( index , SeekOrigin . Begin ) ;
46
+ if ( ! Parse ( stream ) )
47
+ throw new InvalidDataException ( nameof ( data ) ) ;
48
+ }
60
49
61
- Value = valueList . ToArray ( ) ;
62
- }
63
- else
64
- {
65
- // TODO: Get more granular based on type
66
- Value = data . ReadBytes ( ref index , ( int ) Length ) ;
67
- }
50
+ /// <summary>
51
+ /// Read from the source data stream
52
+ /// </summary>
53
+ /// <param name="data">Stream representing data to read</param>
54
+ public TypeLengthValue ( Stream data )
55
+ {
56
+ if ( ! Parse ( data ) )
57
+ throw new InvalidDataException ( nameof ( data ) ) ;
68
58
}
69
59
70
60
/// <summary>
@@ -213,52 +203,101 @@ public string Format(int paddingLevel = 0)
213
203
return formatBuilder . ToString ( ) ;
214
204
}
215
205
206
+ /// <summary>
207
+ /// Parse a stream into TLV data
208
+ /// </summary>
209
+ /// <param name="data">Stream representing data to read</param>
210
+ /// <returns>Indication if parsing was successful</returns>
211
+ private bool Parse ( Stream data )
212
+ {
213
+ // If the data is invalid
214
+ if ( data . Length == 0 || ! data . CanRead )
215
+ return false ;
216
+ if ( data . Position < 0 || data . Position >= data . Length )
217
+ throw new IndexOutOfRangeException ( nameof ( data ) ) ;
218
+
219
+ // Get the type and modifiers
220
+ Type = ( ASN1Type ) data . ReadByteValue ( ) ;
221
+
222
+ // If we have an end indicator, we just return
223
+ if ( Type == ASN1Type . V_ASN1_EOC )
224
+ return true ;
225
+
226
+ // Get the length of the value
227
+ Length = ReadLength ( data ) ;
228
+
229
+ // Read the value
230
+ #if NET20 || NET35
231
+ if ( ( Type & ASN1Type . V_ASN1_CONSTRUCTED ) != 0 )
232
+ #else
233
+ if ( Type . HasFlag ( ASN1Type . V_ASN1_CONSTRUCTED ) )
234
+ #endif
235
+ {
236
+ var valueList = new List < TypeLengthValue > ( ) ;
237
+
238
+ long currentIndex = data . Position ;
239
+ while ( data . Position < currentIndex + ( long ) Length )
240
+ {
241
+ valueList . Add ( new TypeLengthValue ( data ) ) ;
242
+ }
243
+
244
+ Value = valueList . ToArray ( ) ;
245
+ }
246
+ else
247
+ {
248
+ // TODO: Get more granular based on type
249
+ Value = data . ReadBytes ( ( int ) Length ) ;
250
+ }
251
+
252
+ return true ;
253
+ }
254
+
216
255
/// <summary>
217
256
/// Reads the length field for a type
218
257
/// </summary>
219
- /// <param name="data">Byte array representing data to read</param>
220
- /// <param name="index">Index within the array to read at</param>
258
+ /// <param name="data">Stream representing data to read</param>
221
259
/// <returns>The length value read from the array</returns>
222
- private static ulong ReadLength ( byte [ ] data , ref int index )
260
+ private static ulong ReadLength ( Stream data )
223
261
{
224
- // If we have invalid data, throw an exception
225
- if ( data == null || index < 0 && index >= data . Length )
226
- throw new ArgumentException ( ) ;
262
+ // If the data is invalid
263
+ if ( data . Length == 0 || ! data . CanRead )
264
+ throw new DataException ( nameof ( data ) ) ;
265
+ if ( data . Position < 0 || data . Position >= data . Length )
266
+ throw new IndexOutOfRangeException ( nameof ( data ) ) ;
227
267
228
268
// Read the first byte, assuming it's the length
229
- byte length = data [ index ++ ] ;
269
+ byte length = data . ReadByteValue ( ) ;
230
270
231
271
// If the bit 7 is not set, then use the value as it is
232
272
if ( ( length & 0x80 ) == 0 )
233
273
return length ;
234
274
235
275
// Otherwise, use the value as the number of remaining bytes to read
236
276
int bytesToRead = length & ~ 0x80 ;
237
- byte [ ] ? bytesRead = data . ReadBytes ( ref index , bytesToRead ) ?? throw new InvalidOperationException ( ) ;
238
-
239
- // TODO: Write extensions to read big-endian
240
-
241
- // Reverse the bytes to be in big-endian order
242
- Array . Reverse ( bytesRead ) ;
243
-
244
- switch ( bytesRead . Length )
277
+ switch ( bytesToRead )
245
278
{
279
+ // Powers of 2
246
280
case 1 :
247
- return bytesRead [ 0 ] ;
281
+ return data . ReadByteValue ( ) ;
248
282
case 2 :
249
- return BitConverter . ToUInt16 ( bytesRead , 0 ) ;
250
- case 3 :
251
- Array . Resize ( ref bytesRead , 4 ) ;
252
- goto case 4 ;
283
+ return data . ReadUInt16BigEndian ( ) ;
253
284
case 4 :
254
- return BitConverter . ToUInt32 ( bytesRead , 0 ) ;
285
+ return data . ReadUInt32BigEndian ( ) ;
286
+ case 8 :
287
+ return data . ReadUInt64BigEndian ( ) ;
288
+
289
+ // Values between are assembled
290
+ case 3 :
291
+ byte [ ] lessThan4 = data . ReadBytes ( bytesToRead ) ;
292
+ Array . Resize ( ref lessThan4 , 4 ) ;
293
+ return data . ReadUInt32BigEndian ( ) ;
255
294
case 5 :
256
295
case 6 :
257
296
case 7 :
258
- Array . Resize ( ref bytesRead , 8 ) ;
259
- goto case 8 ;
260
- case 8 :
261
- return BitConverter . ToUInt64 ( bytesRead , 0 ) ;
297
+ byte [ ] lessThan8 = data . ReadBytes ( bytesToRead ) ;
298
+ Array . Resize ( ref lessThan8 , 8 ) ;
299
+ return data . ReadUInt64BigEndian ( ) ;
300
+
262
301
default :
263
302
throw new InvalidOperationException ( ) ;
264
303
}
0 commit comments