33// See the LICENSE file in the project root for more information.
44
55using System . Buffers ;
6+ using System . Collections . Concurrent ;
67using System . Collections . Generic ;
78using System . Diagnostics ;
89using System . Linq ;
910using System . Numerics ;
1011using System . Reflection ;
12+ using System . Runtime . CompilerServices ;
1113
1214namespace System . Security . Cryptography . Asn1
1315{
@@ -59,6 +61,9 @@ internal static class AsnSerializer
5961 private delegate object Deserializer ( AsnReader reader ) ;
6062 private delegate bool TryDeserializer < T > ( AsnReader reader , out T value ) ;
6163
64+ private static readonly ConcurrentDictionary < Type , FieldInfo [ ] > s_orderedFields =
65+ new ConcurrentDictionary < Type , FieldInfo [ ] > ( ) ;
66+
6267 private static Deserializer TryOrFail < T > ( TryDeserializer < T > tryDeserializer )
6368 {
6469 return reader =>
@@ -70,6 +75,53 @@ private static Deserializer TryOrFail<T>(TryDeserializer<T> tryDeserializer)
7075 } ;
7176 }
7277
78+ private static FieldInfo [ ] GetOrderedFields ( Type typeT )
79+ {
80+ return s_orderedFields . GetOrAdd (
81+ typeT ,
82+ t =>
83+ {
84+ // https://github.com/dotnet/corefx/issues/14606 asserts that ordering by the metadata
85+ // token on a SequentialLayout will produce the fields in their layout order.
86+ //
87+ // Some other alternatives:
88+ // * Add an attribute for controlling the field read order.
89+ // fieldInfos.Select(fi => (fi, fi.GetCustomAttribute<AsnFieldOrderAttribute>(false)).
90+ // Where(val => val.Item2 != null).OrderBy(val => val.Item2.OrderWeight).Select(val => val.Item1);
91+ //
92+ // * Use Marshal.OffsetOf as a sort key
93+ //
94+ // * Some sort of interface to return the fields in a declared order, using either
95+ // an existing object, or Activator.CreateInstance. It would need to check that
96+ // any returned fields actually were declared on the type that was queried.
97+ //
98+ // * Invent more alternatives
99+ FieldInfo [ ] fieldInfos = t . GetFields ( FieldFlags ) ;
100+
101+ if ( fieldInfos . Length == 0 )
102+ {
103+ return Array . Empty < FieldInfo > ( ) ;
104+ }
105+
106+ try
107+ {
108+ int token = fieldInfos [ 0 ] . MetadataToken ;
109+ }
110+ catch ( InvalidOperationException )
111+ {
112+ // If MetadataToken isn't available (like in ILC) then just hope that
113+ // the fields are returned in declared order. For the most part that
114+ // will result in data misaligning to fields and deserialization failing,
115+ // thus a CryptographicException.
116+ return fieldInfos ;
117+ }
118+
119+ Array . Sort ( fieldInfos , ( x , y ) => x . MetadataToken . CompareTo ( y . MetadataToken ) ) ;
120+ return fieldInfos ;
121+ } ) ;
122+ }
123+
124+
73125 private static ChoiceAttribute GetChoiceAttribute ( Type typeT )
74126 {
75127 ChoiceAttribute attr = typeT . GetCustomAttribute < ChoiceAttribute > ( inherit : false ) ;
@@ -102,20 +154,7 @@ private static void PopulateChoiceLookup(
102154 Type typeT ,
103155 LinkedList < FieldInfo > currentSet )
104156 {
105- FieldInfo [ ] fieldInfos = typeT . GetFields ( FieldFlags ) ;
106-
107- // https://github.com/dotnet/corefx/issues/14606 asserts that ordering by the metadata
108- // token on a SequentialLayout will produce the fields in their layout order.
109- //
110- // Some other alternatives:
111- // * Add an attribute for controlling the field read order.
112- // fieldInfos.Select(fi => (fi, fi.GetCustomAttribute<AsnFieldOrderAttribute>(false)).
113- // Where(val => val.Item2 != null).OrderBy(val => val.Item2.OrderWeight).Select(val => val.Item1);
114- //
115- // * Use Marshal.OffsetOf as a sort key
116- //
117- // * Invent more alternatives
118- foreach ( FieldInfo fieldInfo in fieldInfos . OrderBy ( fi => fi . MetadataToken ) )
157+ foreach ( FieldInfo fieldInfo in GetOrderedFields ( typeT ) )
119158 {
120159 Type fieldType = fieldInfo . FieldType ;
121160
@@ -211,7 +250,7 @@ private static void SerializeChoice(Type typeT, object value, AsnWriter writer)
211250 }
212251 else
213252 {
214- FieldInfo [ ] fieldInfos = typeT . GetFields ( FieldFlags ) ;
253+ FieldInfo [ ] fieldInfos = GetOrderedFields ( typeT ) ;
215254
216255 for ( int i = 0 ; i < fieldInfos . Length ; i ++ )
217256 {
0 commit comments