Enumerating Evidence

The Evidence class supports being enumerated in three different ways:

The first two are pretty self explanatory, enumerating over the evidence that the assembly supplied itself, or over the evidence supplied by the CLR and it's hosting application.  However, Evidence is also IEnumerable which means it allows you to do an unqualified GetEnumerator.

Using the unqualified GetEnumerator method however is almost certainly always a bug.  (Which is unfortunate, since this is the method that gets called if you're using the foreach syntax to enumerate Evidence).  GetEnumerator will iterate over all of the host evidence followed by all of the evidence supplied by the Assembly.  The problem is that there is no way to differentiate between these two classes of evidence and they have two entirely separate levels of trustworthiness.  One is coming from the CLR, saying "This assembly was loaded from https://www.evilweb.com/MaliciousAssembly.dll", the other is coming from that assembly saying "I was loaded from https://www.trustworthy.com/SafeAssembly.dll".  Obviously we trust the host's evidence much more than we trust the assembly's version.

What if I decide to be clever then, and if I see two evidence objects of the same type only trust the one I see first?  Since the host evidence is enumerated before the assembly evidence, presumably this will protect me from choosing a malicious assembly's version.  However, this leaves me unprotected in the case that the host did not supply evidence of  a specific type.  For instance, the CLR will only give Publisher evidence for assemblies with an Authenticode signature that has been verified.  An unsigned assembly (or one that's been signed, then tampered with), will not have Publisher appear in their host evidence list.  However they could certainly add it to their Assembly list, and it would fool this "choose-first-seen" algorithm.

So if you're looking to make a trust decision based upon an Assembly's Evidence collection, it's very important to iterate each class of evidence separately. You do end up loosing the convince of foreach, but standard enumeration of evidence cannot be done safely if a trust decision is being made based upon those evidence objects.  (In fact, I wouldn't be surprised to see the standard GetEnumerator implementation obsoleted in a future release of the framework ...)

        IEnumerator hostEvidence = assembly.Evidence.GetHostEnumerator();

        while (hostEvidence.MoveNext())

        {

            object hostEvidenceObject = hostEvidence.Current;

 

            //

            // hostEvidenceObject can trusted and decisions can be made based upon it

            //

        }

 

        IEnumerator assemblyEvidence = assembly.Evidence.GetAssemblyEnumerator();

        while (assemblyEvidence.MoveNext())

        {

            object assemblyEvidenceObject = assemblyEvidence.Current;

 

            //

            // assemblyEvidenceObject cannot be trusted, and should be verified in some way

            //

        }