Skip to content

Commit 644db6d

Browse files
committed
Fixed reported issue: Crypt32/PSPKI#59 by adding restrictions to nested types in primitive types
1 parent 6ebe9b0 commit 644db6d

2 files changed

Lines changed: 48 additions & 13 deletions

File tree

Asn1Parser/Asn1Parser.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
<Company>Sysadmins LV</Company>
77
<Description>ASN.1 binary parser and utility set for binary data encoded with Distinguished Encoding Rules (DER).</Description>
88
<Copyright>Copyright © Sysadmins LV 2012 - 2018</Copyright>
9-
<AssemblyVersion>1.2.3.22</AssemblyVersion>
10-
<FileVersion>1.2.3.22</FileVersion>
9+
<AssemblyVersion>1.2.3.24</AssemblyVersion>
10+
<FileVersion>1.2.3.24</FileVersion>
1111
<AssemblyName>SysadminsLV.Asn1Parser</AssemblyName>
1212
<RootNamespace>SysadminsLV.Asn1Parser</RootNamespace>
1313
<PackageLicenseUrl>https://github.com/Crypt32/Asn1DerParser.NET/blob/master/LICENSE.md</PackageLicenseUrl>

Asn1Parser/Asn1Reader.cs

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,10 @@ void m_initialize(Byte[] raw, Int32 pOffset) {
128128
if (Tag == 0) {
129129
throw new Asn1InvalidTagException(Offset);
130130
}
131-
if (_multiNestedTypes.Contains(Tag) || (Tag & (Byte) Asn1Class.CONSTRUCTED) > 0) {
131+
// the idea is that SET/SEQUENCE and any explicitly constructed types are constructed by default.
132+
// Though, we need to limit them for Application and higher classes which are not guaranteed to be
133+
// constructed.
134+
if (_multiNestedTypes.Contains(Tag) || ((Tag & (Byte)Asn1Class.CONSTRUCTED) > 0 && Tag < (Byte)Asn1Class.APPLICATION)) {
132135
IsConstructed = true;
133136
}
134137
if (PayloadLength == 0) {
@@ -159,27 +162,43 @@ void parseNestedType() {
159162
// processing rules (assuming zero-based bits):
160163
// if bit 5 is set to "1", or the type is SEQUENCE/SET -- the type is constructed. Unroll nested types.
161164
// if bit 5 is set to "0", attempt to resolve nested types only for UNIVERSAL tags.
165+
// some universal types cannot include nested types: skip them in advance.
162166
if (_excludedTags.Contains(Tag) || PayloadLength < 2) { return; }
163167
Int64 pstart = PayloadStartOffset;
164168
Int32 plength = PayloadLength;
165-
if (Tag == 3) {
169+
// BIT_STRING includes "unused bits" octet, do not count it in calculations
170+
if (Tag == (Byte)Asn1Type.BIT_STRING) {
166171
pstart = PayloadStartOffset + 1;
167172
plength = PayloadLength - 1;
168173
}
174+
// if current type is constructed or nestable by default
169175
if (IsConstructed) {
176+
// check if map for current type exists
170177
if (!_offsetMap.ContainsKey(pstart)) {
178+
// if current map doesn't contain nested types boundaries, add them to the map.
179+
// this condition occurs when we face current type for the first time.
171180
predict(pstart, plength, true, out childCount);
172181
}
173-
174182
return;
175183
}
184+
// universal types can contain only universal or constructed nested types.
185+
if (!testNestedForUniversal(pstart)) {
186+
return;
187+
}
188+
// attempt to unroll nested type
189+
IsConstructed = predict(pstart, plength, false, out childCount);
190+
// reiterate again and build map for children
191+
if (IsConstructed && !_offsetMap.ContainsKey(pstart)) {
192+
predict(pstart, plength, true, out childCount);
193+
}
194+
}
195+
Boolean testNestedForUniversal(Int64 pstart) {
196+
// if current type is primitive, then nested type can be either, primitive or constructed only.
176197
if (Tag > 0 && Tag < (Byte)Asn1Type.TAG_MASK) {
177-
IsConstructed = predict(pstart, plength, false, out childCount);
178-
// reiterate again and build map for children
179-
if (IsConstructed && !_offsetMap.ContainsKey(pstart)) {
180-
predict(pstart, plength, true, out childCount);
181-
}
198+
return RawData[pstart] < (Byte)Asn1Class.APPLICATION;
182199
}
200+
// otherwise return True, so we can proceed with nested type parsing which can be of any type.
201+
return true;
183202
}
184203
Boolean predict(Int64 start, Int32 projectedLength, Boolean assignMap, out Int32 estimatedChildCount) {
185204
Int64 levelStart = start;
@@ -236,7 +255,7 @@ Int64 calculatePredictLength(Int64 offset) {
236255
}
237256
void moveAndExpectTypes(Func<Boolean> action, params Byte[] expectedTypes) {
238257
if (expectedTypes == null) { throw new ArgumentNullException(nameof(expectedTypes)); }
239-
HashSet<Byte> htable = new HashSet<Byte>();
258+
var htable = new HashSet<Byte>();
240259
foreach (Byte tag in expectedTypes) {
241260
htable.Add(tag);
242261
}
@@ -292,7 +311,6 @@ public Int32 GetNestedNodeCount() {
292311
/// </returns>
293312
public Boolean MoveNext() {
294313
if (NextOffset == 0) { return false; }
295-
//projectedIterationSize = _offsetMap[NextOffset];
296314
currentPosition = _offsetMap[NextOffset];
297315
m_initialize(null, NextOffset);
298316
return true;
@@ -364,7 +382,8 @@ public void MoveNextCurrentLevelAndExpectTags(params Byte[] expectedTags) {
364382
/// method must be called prior to first call of this method. Subsequent <strong>BuildOffsetMap</strong>
365383
/// method calls are not necessary.
366384
/// </remarks>
367-
public Boolean MoveToPosition(Int32 newPosition) {
385+
[Obsolete("This method contains a typo. Use MoveToPosition instead.")]
386+
public Boolean MoveToPoisition(Int32 newPosition) {
368387
if (_offsetMap == null) {
369388
throw new InvalidOperationException();
370389
}
@@ -376,6 +395,22 @@ public Boolean MoveToPosition(Int32 newPosition) {
376395
return true;
377396
}
378397
/// <summary>
398+
/// Moves to a specified start offset.
399+
/// </summary>
400+
/// <param name="newPosition">ASN structure start position (offset).</param>
401+
/// <returns>
402+
/// <strong>True</strong> if specified offset is valid and pointer was successfully set to specified position,
403+
/// otherwise <strong>False</strong>.
404+
/// </returns>
405+
/// <remarks>
406+
/// Specified position validity is determined based on internal map and <see cref="BuildOffsetMap"/>
407+
/// method must be called prior to first call of this method. Subsequent <strong>BuildOffsetMap</strong>
408+
/// method calls are not necessary.
409+
/// </remarks>
410+
public Boolean MoveToPosition(Int32 newPosition) {
411+
return MoveToPoisition(newPosition);
412+
}
413+
/// <summary>
379414
/// Moves to the beginning of the file.
380415
/// </summary>
381416
public void Reset() {

0 commit comments

Comments
 (0)