File: Symbols\ISymbolExtensions_PerformIVTCheck.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Collections;
 
namespace Microsoft.CodeAnalysis
{
    public static partial class ISymbolExtensions
    {
        /// <summary>
        /// Given that an assembly with identity assemblyGrantingAccessIdentity granted access to assemblyWantingAccess,
        /// check the public keys to ensure the internals-visible-to check should succeed. This is used by both the
        /// C# and VB implementations as a helper to implement `bool IAssemblySymbol.GivesAccessTo(IAssemblySymbol toAssembly)`.
        /// </summary>
        internal static IVTConclusion PerformIVTCheck(
            this AssemblyIdentity assemblyGrantingAccessIdentity,
            ImmutableArray<byte> assemblyWantingAccessKey,
            ImmutableArray<byte> grantedToPublicKey)
        {
            // This gets a bit complicated. Let's break it down.
            //
            // First off, let's assume that the "other" assembly is GrantingAssembly.DLL, that the "this"
            // assembly is "WantingAssembly.DLL", and that GrantingAssembly has named WantingAssembly as a friend (that is a precondition
            // to calling this method). Whether we allow WantingAssembly to see internals of GrantingAssembly depends on these four factors:
            //
            // q1) Is GrantingAssembly strong-named?
            // q2) Did GrantingAssembly name WantingAssembly as a friend via a strong name?
            // q3) Is WantingAssembly strong-named?
            // q4) Does GrantingAssembly give a strong-name for WantingAssembly that matches our strong name?
            //
            // Before we dive into the details, we should mention two additional facts:
            //
            // * If the answer to q1 is "yes", and GrantingAssembly was compiled by a Roslyn compiler, then q2 must be "yes" also.
            //   Strong-named GrantingAssembly must only be friends with strong-named WantingAssembly. See the blog article
            //   http://blogs.msdn.com/b/ericlippert/archive/2009/06/04/alas-smith-and-jones.aspx
            //   for an explanation of why this feature is desirable.
            //
            //   Now, just because the compiler enforces this rule does not mean that we will never run into
            //   a scenario where GrantingAssembly is strong-named and names WantingAssembly via a weak name. Not all assemblies
            //   were compiled with a Roslyn compiler. We still need to deal sensibly with this situation.
            //   We do so by ignoring the problem; if strong-named GrantingAssembly extends friendship to weak-named
            //   WantingAssembly then we're done; any assembly named WantingAssembly is a friend of GrantingAssembly.
            //
            //   Incidentally, the C# compiler produces error CS1726, ERR_FriendAssemblySNReq, and VB produces
            //   the error VB31535, ERR_FriendAssemblyStrongNameRequired, when compiling 
            //   a strong-named GrantingAssembly that names a weak-named WantingAssembly as its friend.
            //
            // * If the answer to q1 is "no" and the answer to q3 is "yes" then we are in a situation where
            //   strong-named WantingAssembly is referencing weak-named GrantingAssembly, which is illegal. In the dev10 compiler
            //   we do not give an error about this until emit time. In Roslyn we have a new error, CS7029,
            //   which we give before emit time when we detect that weak-named GrantingAssembly has given friend access
            //   to strong-named WantingAssembly, which then references GrantingAssembly. However, we still want to give friend
            //   access to WantingAssembly for the purposes of semantic analysis.
            //
            // Roslyn C# does not yet give an error in other circumstances whereby a strong-named assembly
            // references a weak-named assembly. See https://github.com/dotnet/roslyn/issues/26722
            //
            // Let's make a chart that illustrates all the possible answers to these four questions, and
            // what the resulting accessibility should be:
            //
            // case q1  q2  q3  q4  Result                 Explanation
            // 1    YES YES YES YES SUCCESS          GrantingAssembly has named this strong-named WantingAssembly as a friend.
            // 2    YES YES YES NO  NO MATCH         GrantingAssembly has named a different strong-named WantingAssembly as a friend.
            // 3    YES YES NO  NO  NO MATCH         GrantingAssembly has named a strong-named WantingAssembly as a friend, but this WantingAssembly is weak-named.
            // 4    YES NO  YES NO  SUCCESS          GrantingAssembly has improperly (*) named any WantingAssembly as its friend. But we honor its offer of friendship.
            // 5    YES NO  NO  NO  SUCCESS          GrantingAssembly has improperly (*) named any WantingAssembly as its friend. But we honor its offer of friendship.
            // 6    NO  YES YES YES SUCCESS, BAD REF GrantingAssembly has named this strong-named WantingAssembly as a friend, but WantingAssembly should not be referring to a weak-named GrantingAssembly.
            // 7    NO  YES YES NO  NO MATCH         GrantingAssembly has named a different strong-named WantingAssembly as a friend.
            // 8    NO  YES NO  NO  NO MATCH         GrantingAssembly has named a strong-named WantingAssembly as a friend, but this WantingAssembly is weak-named.
            // 9    NO  NO  YES NO  SUCCESS, BAD REF GrantingAssembly has named any WantingAssembly as a friend, but WantingAssembly should not be referring to a weak-named GrantingAssembly.
            // 10   NO  NO  NO  NO  SUCCESS          GrantingAssembly has named any WantingAssembly as its friend.
            //                                     
            // (*) GrantingAssembly was not built with a Roslyn compiler, which would have prevented this.
            //
            // This method never returns NoRelationshipClaimed because if control got here, then we assume
            // (as a precondition) that GrantingAssembly named WantingAssembly as a friend somehow.
 
            bool q1 = assemblyGrantingAccessIdentity.IsStrongName;
            bool q2 = !grantedToPublicKey.IsDefaultOrEmpty;
            bool q3 = !assemblyWantingAccessKey.IsDefaultOrEmpty;
            bool q4 = (q2 & q3) && ByteSequenceComparer.Equals(grantedToPublicKey, assemblyWantingAccessKey);
 
            // Cases 2, 3, 7 and 8:
            if (q2 && !q4)
            {
                return IVTConclusion.PublicKeyDoesntMatch;
            }
 
            // Cases 6 and 9:
            if (!q1 && q3)
            {
                return IVTConclusion.OneSignedOneNot;
            }
 
            // Cases 1, 4, 5 and 10:
            return IVTConclusion.Match;
        }
    }
}