private bool InvokeVerifyAction()

in src/BinSkim.Rules/PERules/BA2022.SignSecurely.cs [69:187]


        private bool InvokeVerifyAction(
            BinaryAnalyzerContext context,
            string filePath,
            out Native.WINTRUST_DATA winTrustData,
            out string algorithmsText)
        {
            Guid action;
            CryptoError cryptoError;
            var badAlgorithms = new List<Tuple<string, string>>();
            var goodAlgorithms = new List<Tuple<string, string>>();

            algorithmsText = null;
            action = Native.ActionGenericVerifyV2;

            uint signatureCount = 1;

            // First, we retrieve the signature count
            winTrustData = this.InitializeWinTrustDataStruct(filePath, WinTrustDataKind.SignatureCount);
            Native.WinVerifyTrustWrapper(Native.INVALID_HANDLE_VALUE, ref action, ref winTrustData);

            if (winTrustData.pSignatureSettings != IntPtr.Zero)
            {
                Native.WINTRUST_SIGNATURE_SETTINGS signatureSettings = Marshal.PtrToStructure<Native.WINTRUST_SIGNATURE_SETTINGS>(winTrustData.pSignatureSettings);
                signatureCount = signatureSettings.cSecondarySigs + 1; // Total count primary + cSecondary
            }

            this.InvokeCloseAction(winTrustData);

            // First, we will invoke the basic verification on all returned
            // signatures. Note that currently this code path does not reach across
            // the network to perform its function. We could optionally
            // enable this (which would require altering the code that initializes
            // our WINTRUST_DATA instance).

            for (uint i = 0; i < signatureCount; i++)
            {
                string hashAlgorithm, hashEncryptionAlgorithm;
                winTrustData = this.InitializeWinTrustDataStruct(filePath, WinTrustDataKind.EnforcePolicy, i);

                cryptoError = (CryptoError)Native.WinVerifyTrustWrapper(Native.INVALID_HANDLE_VALUE, ref action, ref winTrustData);

                switch (cryptoError)
                {
                    // The SignSecurely check mostly validates signing algorithm strength. The
                    // error conditions are expected in some scan contexts, for example, an 
                    // isolated build environment which hasn't been configured to trust the
                    // signing root. Providing a more complex signing validation would require
                    // BinSkim to be significantly more configurable to provide information on
                    // the scan environment as well as the scan targets.
                    case CryptoError.CERT_E_UNTRUSTEDROOT:
                    case CryptoError.CERT_E_CHAINING:
                    case CryptoError.ERROR_SUCCESS:
                    {
                        // Hash that represents the subject is trusted.
                        // Trusted publisher with no verification errors.
                        // No publisher or time stamp errors.
                        // This verification excludes root chain info.
                        if (this.GetSignerHashAlgorithms(context, winTrustData, out hashAlgorithm, out hashEncryptionAlgorithm))
                        {
                            goodAlgorithms.Add(new Tuple<string, string>(hashAlgorithm, hashEncryptionAlgorithm));
                        }

                        this.InvokeCloseAction(winTrustData);
                        break;
                    }

                    case CryptoError.NTE_BAD_ALGID:
                    {
                        this.InvokeCloseAction(winTrustData);

                        // We cannot retrieve algorithm id and cert info for images that fail
                        // the stringent WinTrustVerify security check. We therefore start
                        // a new call chain with looser validation criteria.
                        winTrustData = this.InitializeWinTrustDataStruct(filePath, WinTrustDataKind.Normal);
                        Native.WinVerifyTrustWrapper(Native.INVALID_HANDLE_VALUE, ref action, ref winTrustData);

                        if (this.GetSignerHashAlgorithms(context, winTrustData, out hashAlgorithm, out hashEncryptionAlgorithm))
                        {
                            badAlgorithms.Add(new Tuple<string, string>(hashAlgorithm, hashEncryptionAlgorithm));
                        }

                        this.InvokeCloseAction(winTrustData);
                        break;
                    }

                    case CryptoError.TRUST_E_NOSIGNATURE:
                    {
                        Notes.LogNotApplicableToSpecifiedTarget(context, MetadataConditions.ImageIsNotSigned);
                        return false;
                    }

                    default:
                    {
                        string cryptoErrorDescription = cryptoError.GetErrorDescription();
                        // '{0}' signing was flagged as insecure by WinTrustVerify with error code: '{1}' ({2})
                        context.Logger.Log(this, RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                            nameof(RuleResources.BA2022_Error_DidNotVerify),
                            context.TargetUri.GetFileName(),
                            cryptoError.ToString(),
                            cryptoErrorDescription));
                        this.InvokeCloseAction(winTrustData);
                        return false;
                    }
                }
            }

            algorithmsText = this.BuildAlgorithmsText(badAlgorithms, goodAlgorithms);

            if (goodAlgorithms.Count == 0)
            {
                // '{0}' was signed exclusively with algorithms that WinTrustVerify has flagged as insecure. {1}
                context.Logger.Log(this, RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                    nameof(RuleResources.BA2022_Error_BadSigningAlgorithm),
                    context.TargetUri.GetFileName(),
                    algorithmsText));
            }

            return goodAlgorithms.Count > 0;
        }