Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
CatalogHelper.cs842 linesDownload Raw Back to security
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#if !UNIX5 6using System.Security.Cryptography;7using System.Collections.Generic;8using System.Collections.ObjectModel;9using System.ComponentModel;10using System.IO;11using System.Linq;12using System.Management.Automation.Internal;13using System.Management.Automation.Security;14using System.Management.Automation.Win32Native;15using System.Runtime.InteropServices;16 17namespace System.Management.Automation18{19    /// <summary>20    /// Defines the possible status when validating integrity of catalog.21    /// </summary>22    public enum CatalogValidationStatus23    {24        /// <summary>25        /// Status when catalog is not tampered.26        /// </summary>27        Valid,28 29        /// <summary>30        /// Status when catalog is tampered.31        /// </summary>32        ValidationFailed33    }34 35    /// <summary>36    /// Object returned by Catalog Cmdlets.37    /// </summary>38    public class CatalogInformation39    {40        /// <summary>41        /// Status of catalog.42        /// </summary>43        public CatalogValidationStatus Status { get; set; }44 45        /// <summary>46        /// Hash Algorithm used to calculate the hashes of files in Catalog.47        /// </summary>48        public string HashAlgorithm { get; set; }49 50        /// <summary>51        /// Dictionary mapping files relative paths to their hash values found from Catalog.52        /// </summary>53        public Dictionary<string, string> CatalogItems { get; set; }54 55        /// <summary>56        /// Dictionary mapping files relative paths to their hash values.57        /// </summary>58        public Dictionary<string, string> PathItems { get; set; }59 60        /// <summary>61        /// Signature for the catalog.62        /// </summary>63        public Signature Signature { get; set; }64    }65 66    /// <summary>67    /// Helper functions for Windows Catalog functionality.68    /// </summary>69    internal static class CatalogHelper70    {71        // Catalog Version is (0X100 = 256) for Catalog Version 172        private const int catalogVersion1 = 256;73 74        // Catalog Version is (0X200 = 512) for Catalog Version 275        private const int catalogVersion2 = 512;76 77        // Hash Algorithms supported by Windows Catalog78        private const string HashAlgorithmSHA1 = "SHA1";79        private const string HashAlgorithmSHA256 = "SHA256";80        private static PSCmdlet _cmdlet = null;81 82        /// <summary>83        /// Find out the Version of Catalog by reading its Meta data. We can have either version 1 or version 2 catalog.84        /// </summary>85        /// <param name="catalogHandle">Handle to open catalog file.</param>86        /// <returns>Version of the catalog.</returns>87        private static int GetCatalogVersion(SafeCATHandle catalogHandle)88        {89            int catalogVersion = -1;90 91            WinTrustMethods.CRYPTCATSTORE catalogInfo = WinTrustMethods.CryptCATStoreFromHandle(catalogHandle);92 93            if (catalogInfo.dwPublicVersion == catalogVersion2)94            {95                catalogVersion = 2;96            }97            // One Windows 7 this API sent version information as decimal 1 not hex (0X100 = 256)98            // so we are checking for that value as well. Reason we are not checking for version 2 above in99            // this scenario because catalog version 2 is not supported on win7.100            else if ((catalogInfo.dwPublicVersion == catalogVersion1) || (catalogInfo.dwPublicVersion == 1))101            {102                catalogVersion = 1;103            }104            else105            {106                // catalog version we don't understand107                Exception exception = new InvalidOperationException(StringUtil.Format(CatalogStrings.UnKnownCatalogVersion,108                                      catalogVersion1.ToString("X"),109                                      catalogVersion2.ToString("X")));110 111                ErrorRecord errorRecord = new ErrorRecord(exception, "UnKnownCatalogVersion", ErrorCategory.InvalidOperation, null);112                _cmdlet.ThrowTerminatingError(errorRecord);113            }114 115            return catalogVersion;116        }117 118        /// <summary>119        /// HashAlgorithm used by the Catalog. It is based on the version of Catalog.120        /// </summary>121        /// <param name="catalogVersion">Path of the output catalog file.</param>122        /// <returns>Version of the catalog.</returns>123        private static string GetCatalogHashAlgorithm(int catalogVersion)124        {125            string hashAlgorithm = string.Empty;126 127            if (catalogVersion == 1)128            {129                hashAlgorithm = HashAlgorithmSHA1;130            }131            else if (catalogVersion == 2)132            {133                hashAlgorithm = HashAlgorithmSHA256;134            }135            else136            {137                // version we don't understand138                Exception exception = new InvalidOperationException(StringUtil.Format(CatalogStrings.UnKnownCatalogVersion,139                                      "1.0",140                                      "2.0"));141 142                ErrorRecord errorRecord = new ErrorRecord(exception, "UnKnownCatalogVersion", ErrorCategory.InvalidOperation, null);143                _cmdlet.ThrowTerminatingError(errorRecord);144            }145 146            return hashAlgorithm;147        }148 149        /// <summary>150        /// Generate the Catalog Definition File representing files and folders.151        /// </summary>152        /// <param name="Path">Path of expected output .cdf file.</param>153        /// <param name="catalogFilePath">Path of the output catalog file.</param>154        /// <param name="cdfFilePath">Path of the catalog definition file.</param>155        /// <param name="catalogVersion">Version of catalog.</param>156        /// <param name="hashAlgorithm">Hash method used to generate hashes for the Catalog.</param>157        /// <returns>HashSet for the relative Path for files in Catalog.</returns>158        internal static string GenerateCDFFile(Collection<string> Path, string catalogFilePath, string cdfFilePath, int catalogVersion, string hashAlgorithm)159        {160            HashSet<string> relativePaths = new HashSet<string>();161 162            string cdfHeaderContent = string.Empty;163            string cdfFilesContent = string.Empty;164            int catAttributeCount = 0;165 166            // First create header and files section for the catalog then write in file167            cdfHeaderContent += "[CatalogHeader]" + Environment.NewLine;168            cdfHeaderContent += @"Name=" + catalogFilePath + Environment.NewLine;169            cdfHeaderContent += "CatalogVersion=" + catalogVersion + Environment.NewLine;170            cdfHeaderContent += "HashAlgorithms=" + hashAlgorithm + Environment.NewLine;171            cdfFilesContent += "[CatalogFiles]" + Environment.NewLine;172 173            foreach (string catalogFile in Path)174            {175                if (System.IO.Directory.Exists(catalogFile))176                {177                    var directoryItems = Directory.EnumerateFiles(catalogFile, "*.*", SearchOption.AllDirectories);178                    foreach (string fileItem in directoryItems)179                    {180                        ProcessFileToBeAddedInCatalogDefinitionFile(new FileInfo(fileItem), new DirectoryInfo(catalogFile), ref relativePaths, ref cdfHeaderContent, ref cdfFilesContent, ref catAttributeCount);181                    }182                }183                else if (System.IO.File.Exists(catalogFile))184                {185                    ProcessFileToBeAddedInCatalogDefinitionFile(new FileInfo(catalogFile), null, ref relativePaths, ref cdfHeaderContent, ref cdfFilesContent, ref catAttributeCount);186                }187            }188 189            using (System.IO.StreamWriter fileWriter = new System.IO.StreamWriter(new FileStream(cdfFilePath, FileMode.Create)))190            {191                fileWriter.WriteLine(cdfHeaderContent);192                fileWriter.WriteLine();193                fileWriter.WriteLine(cdfFilesContent);194            }195 196            return cdfFilePath;197        }198 199        /// <summary>200        /// Get file attribute (Relative path in our case) from catalog.201        /// </summary>202        /// <param name="fileToHash">File to hash.</param>203        /// <param name="dirInfo">Directory information about file needed to calculate relative file path.</param>204        /// <param name="relativePaths">Working set of relative paths of all files.</param>205        /// <param name="cdfHeaderContent">Content to be added in CatalogHeader section of cdf File.</param>206        /// <param name="cdfFilesContent">Content to be added in CatalogFiles section of cdf File.</param>207        /// <param name="catAttributeCount">Indicating the current no of catalog header level attributes.</param>208        /// <returns>Void.</returns>209        internal static void ProcessFileToBeAddedInCatalogDefinitionFile(FileInfo fileToHash, DirectoryInfo dirInfo, ref HashSet<string> relativePaths, ref string cdfHeaderContent, ref string cdfFilesContent, ref int catAttributeCount)210        {211            string relativePath = string.Empty;212 213            if (dirInfo != null)214            {215                // Relative path of the file is the path inside the containing folder excluding folder Name216                relativePath = fileToHash.FullName.AsSpan(dirInfo.FullName.Length).TrimStart('\\').ToString();217            }218            else219            {220                relativePath = fileToHash.Name;221            }222 223            if (relativePaths.Add(relativePath))224            {225                if (fileToHash.Length != 0)226                {227                    cdfFilesContent += "<HASH>" + fileToHash.FullName + "=" + fileToHash.FullName + Environment.NewLine;228                    cdfFilesContent += "<HASH>" + fileToHash.FullName + "ATTR1=0x10010001:FilePath:" + relativePath + Environment.NewLine;229                }230                else231                {232                    // zero length files are added as catalog level attributes because they can not be hashed233                    cdfHeaderContent += "CATATTR" + (++catAttributeCount) + "=0x10010001:FilePath:" + relativePath + Environment.NewLine;234                }235            }236            else237            {238                // If Files have same relative paths we can not distinguish them for239                // Validation. So failing.240                ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFilesRelativePath, relativePath)), "FoundDuplicateFilesRelativePath", ErrorCategory.InvalidOperation, null);241                _cmdlet.ThrowTerminatingError(errorRecord);242            }243        }244        /// <summary>245        /// Generate the Catalog file for Input Catalog Definition File.246        /// </summary>247        /// <param name="cdfFilePath">Path to the Input .cdf file.</param>248        internal static void GenerateCatalogFile(string cdfFilePath)249        {250            // Open CDF File251            SafeCATCDFHandle resultCDF;252            try253            {254                resultCDF = WinTrustMethods.CryptCATCDFOpen(cdfFilePath, ParseErrorCallback);255            }256            catch (Win32Exception e)257            {258                // If we are not able to open CDF file we can not continue generating catalog259                ErrorRecord errorRecord = new ErrorRecord(260                    new InvalidOperationException(CatalogStrings.UnableToOpenCatalogDefinitionFile, e),261                    "UnableToOpenCatalogDefinitionFile",262                    ErrorCategory.InvalidOperation,263                    null);264                _cmdlet.ThrowTerminatingError(errorRecord);265                return;266            }267 268            // navigate CDF header and files sections269            using (resultCDF)270            {271                // First navigate all catalog level attributes entries first, they represent zero size files272                IntPtr catalogAttr = IntPtr.Zero;273                do274                {275                    catalogAttr = WinTrustMethods.CryptCATCDFEnumCatAttributes(resultCDF, catalogAttr, ParseErrorCallback);276 277                    if (catalogAttr != IntPtr.Zero)278                    {279                        string filePath = ProcessFilePathAttributeInCatalog(catalogAttr);280                        _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.AddFileToCatalog, filePath, filePath));281                    }282                } while (catalogAttr != IntPtr.Zero);283 284                // navigate all the files hash entries in the .cdf file285                IntPtr memberInfo = IntPtr.Zero;286                IntPtr memberFile = IntPtr.Zero;287                string fileName = string.Empty;288                do289                {290                    memberFile = WinTrustMethods.CryptCATCDFEnumMembersByCDFTagEx(resultCDF, memberFile, ParseErrorCallback, ref memberInfo,291                        fContinueOnError: true, pvReserved: IntPtr.Zero);292                    fileName = Marshal.PtrToStringUni(memberFile);293 294                    if (!string.IsNullOrEmpty(fileName))295                    {296                        IntPtr memberAttr = IntPtr.Zero;297                        string fileRelativePath = string.Empty;298                        do299                        {300                            memberAttr = WinTrustMethods.CryptCATCDFEnumAttributesWithCDFTag(resultCDF, memberFile, memberInfo, memberAttr, ParseErrorCallback);301 302                            if (memberAttr != IntPtr.Zero)303                            {304                                fileRelativePath = ProcessFilePathAttributeInCatalog(memberAttr);305                                if (!string.IsNullOrEmpty(fileRelativePath))306                                {307                                    // Found the attribute we are looking for308                                    // Filename we read from the above API has <Hash> appended to its name as per CDF file tags convention309                                    // Truncating that Information from the string.310                                    string itemName = fileName.Substring(6);311                                    _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.AddFileToCatalog, itemName, fileRelativePath));312                                    break;313                                }314                            }315                        } while (memberAttr != IntPtr.Zero);316                    }317                } while (fileName != null);318            }319        }320 321        /// <summary>322        /// To generate Catalog for the folder.323        /// </summary>324        /// <param name="Path">Path to folder or File.</param>325        /// <param name="catalogFilePath">Catalog File Path.</param>326        /// <param name="catalogVersion">Catalog File Path.</param>327        /// <param name="cmdlet">Instance of cmdlet calling this method.</param>328        /// <returns>True if able to generate .cat file or false.</returns>329        internal static FileInfo GenerateCatalog(PSCmdlet cmdlet, Collection<string> Path, string catalogFilePath, int catalogVersion)330        {331            _cmdlet = cmdlet;332            string hashAlgorithm = GetCatalogHashAlgorithm(catalogVersion);333 334            if (!string.IsNullOrEmpty(hashAlgorithm))335            {336                // Generate Path for Catalog Definition File337                string cdfFilePath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName());338                cdfFilePath += ".cdf";339                try340                {341                    cdfFilePath = GenerateCDFFile(Path, catalogFilePath, cdfFilePath, catalogVersion, hashAlgorithm);342 343                    if (!File.Exists(cdfFilePath))344                    {345                        // If we are not able to generate catalog definition file we can not continue generating catalog346                        // throw PSTraceSource.NewInvalidOperationException("catalog", CatalogStrings.CatalogDefinitionFileNotGenerated);347                        ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(CatalogStrings.CatalogDefinitionFileNotGenerated), "CatalogDefinitionFileNotGenerated", ErrorCategory.InvalidOperation, null);348                        _cmdlet.ThrowTerminatingError(errorRecord);349                    }350 351                    GenerateCatalogFile(cdfFilePath);352                    if (File.Exists(catalogFilePath))353                    {354                        return new FileInfo(catalogFilePath);355                    }356                }357                finally358                {359                    File.Delete(cdfFilePath);360                }361            }362 363            return null;364        }365 366        /// <summary>367        /// Get file attribute (Relative path in our case) from catalog.368        /// </summary>369        /// <param name="memberAttrInfo">Pointer to current attribute of catalog member.</param>370        /// <returns>Value of the attribute.</returns>371        internal static string ProcessFilePathAttributeInCatalog(IntPtr memberAttrInfo)372        {373            string relativePath = string.Empty;374 375            WinTrustMethods.CRYPTCATATTRIBUTE currentMemberAttr = Marshal.PtrToStructure<WinTrustMethods.CRYPTCATATTRIBUTE>(memberAttrInfo);376 377            // check if this is the attribute we are looking for378            // catalog generated other way not using New-FileCatalog can have attributes we don't understand379            if (currentMemberAttr.pwszReferenceTag.Equals("FilePath", StringComparison.OrdinalIgnoreCase))380            {381                // find the size for the current attribute value and then allocate buffer and copy from byte array382                int attrValueSize = (int)currentMemberAttr.cbValue;383                byte[] attrValue = new byte[attrValueSize];384                Marshal.Copy(currentMemberAttr.pbValue, attrValue, 0, attrValueSize);385                relativePath = System.Text.Encoding.Unicode.GetString(attrValue);386                relativePath = relativePath.TrimEnd('\0');387            }388 389            return relativePath;390        }391 392        /// <summary>393        /// Make a hash for the file.394        /// </summary>395        /// <param name="filePath">Path of the file.</param>396        /// <param name="hashAlgorithm">Used to calculate Hash.</param>397        /// <returns>HashValue for the file.</returns>398        internal static string CalculateFileHash(string filePath, string hashAlgorithm)399        {400            string hashValue = string.Empty;401 402            // To get handle to the hash algorithm to be used to calculate hashes403            SafeCATAdminHandle catAdmin;404            try405            {406                catAdmin = WinTrustMethods.CryptCATAdminAcquireContext2(hashAlgorithm);407            }408            catch (Win32Exception e)409            {410                ErrorRecord errorRecord = new ErrorRecord(411                    new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToAcquireHashAlgorithmContext, hashAlgorithm), e),412                    "UnableToAcquireHashAlgorithmContext",413                    ErrorCategory.InvalidOperation,414                    null);415                _cmdlet.ThrowTerminatingError(errorRecord);416 417                // The method returns an empty string on a failure.418                return hashValue;419            }420 421            // Open the file that is to be hashed for reading and get its handle422            FileStream fileStream;423            try424            {425                fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);426            }427            catch (Exception e)428            {429                // If we are not able to open file that is to be hashed we can not continue with catalog validation430                ErrorRecord errorRecord = new ErrorRecord(431                    new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToReadFileToHash, filePath), e),432                    "UnableToReadFileToHash",433                    ErrorCategory.InvalidOperation,434                    null);435                _cmdlet.ThrowTerminatingError(errorRecord);436 437                // The method returns an empty string on a failure.438                return hashValue;439            }440 441            using (catAdmin)442            using (fileStream)443            {444                byte[] hashBytes = Array.Empty<byte>();445                try446                {447                    hashBytes = WinTrustMethods.CryptCATAdminCalcHashFromFileHandle2(catAdmin, fileStream.SafeFileHandle);448                }449                catch (Win32Exception e)450                {451                    ErrorRecord errorRecord = new ErrorRecord(452                        new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, filePath), e),453                        "UnableToCreateFileHash",454                        ErrorCategory.InvalidOperation,455                        null);456                    _cmdlet.ThrowTerminatingError(errorRecord);457                }458 459                hashValue = Convert.ToHexString(hashBytes);460            }461 462            return hashValue;463        }464 465        /// <summary>466        /// Make list of hashes for given Catalog File.467        /// </summary>468        /// <param name="catalogFilePath">Path to the folder having catalog file.</param>469        /// <param name="excludedPatterns"></param>470        /// <param name="catalogVersion">The version of input catalog we read from catalog meta data after opening it.</param>471        /// <returns>Dictionary mapping files relative paths to HashValues.</returns>472        internal static Dictionary<string, string> GetHashesFromCatalog(string catalogFilePath, WildcardPattern[] excludedPatterns, out int catalogVersion)473        {474            Dictionary<string, string> catalogHashes = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);475            catalogVersion = 0;476 477            SafeCATHandle resultCatalog;478            try479            {480                resultCatalog = WinTrustMethods.CryptCATOpen(catalogFilePath, 0, IntPtr.Zero, 1, 0);481            }482            catch (Win32Exception e)483            {484                ErrorRecord errorRecord = new ErrorRecord(485                    new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToOpenCatalogFile, catalogFilePath), e),486                    "UnableToOpenCatalogFile",487                    ErrorCategory.InvalidOperation,488                    null);489                _cmdlet.ThrowTerminatingError(errorRecord);490                return catalogHashes;491            }492 493            using (resultCatalog)494            {495                IntPtr catAttrInfo = IntPtr.Zero;496 497                // First traverse all catalog level attributes to get information about zero size file.498                do499                {500                    catAttrInfo = WinTrustMethods.CryptCATEnumerateCatAttr(resultCatalog, catAttrInfo);501 502                    // If we found attribute it is a file information retrieve its relative path503                    // and add it to catalog hash collection if its not in excluded files criteria504                    if (catAttrInfo != IntPtr.Zero)505                    {506                        string relativePath = ProcessFilePathAttributeInCatalog(catAttrInfo);507                        if (!string.IsNullOrEmpty(relativePath))508                        {509                            ProcessCatalogFile(relativePath, string.Empty, excludedPatterns, ref catalogHashes);510                        }511                    }512                } while (catAttrInfo != IntPtr.Zero);513 514                catalogVersion = GetCatalogVersion(resultCatalog);515 516                IntPtr memberInfo = IntPtr.Zero;517                // Next Navigate all members in Catalog files and get their relative paths and hashes518                do519                {520                    memberInfo = WinTrustMethods.CryptCATEnumerateMember(resultCatalog, memberInfo);521                    if (memberInfo != IntPtr.Zero)522                    {523                        WinTrustMethods.CRYPTCATMEMBER currentMember = Marshal.PtrToStructure<WinTrustMethods.CRYPTCATMEMBER>(memberInfo);524                        WinTrustMethods.SIP_INDIRECT_DATA pIndirectData = Marshal.PtrToStructure<WinTrustMethods.SIP_INDIRECT_DATA>(currentMember.pIndirectData);525 526                        // For Catalog version 2 CryptoAPI puts hashes of file attributes(relative path in our case) in Catalog as well527                        // We validate those along with file hashes so we are skipping duplicate entries528                        if (!((catalogVersion == 2) && (pIndirectData.DigestAlgorithm.pszObjId.Equals(new Oid("SHA1").Value, StringComparison.OrdinalIgnoreCase))))529                        {530                            string relativePath = string.Empty;531                            IntPtr memberAttrInfo = IntPtr.Zero;532                            do533                            {534                                memberAttrInfo = WinTrustMethods.CryptCATEnumerateAttr(resultCatalog, memberInfo, memberAttrInfo);535 536                                if (memberAttrInfo != IntPtr.Zero)537                                {538                                    relativePath = ProcessFilePathAttributeInCatalog(memberAttrInfo);539                                    if (!string.IsNullOrEmpty(relativePath))540                                    {541                                        break;542                                    }543                                }544                            }545                            while (memberAttrInfo != IntPtr.Zero);546 547                            // If we did not find any Relative Path for the item in catalog we should quit548                            // This catalog must not be valid for our use as catalogs generated using New-FileCatalog549                            // always contains relative file Paths550                            if (string.IsNullOrEmpty(relativePath))551                            {552                                ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToOpenCatalogFile, catalogFilePath)), "UnableToOpenCatalogFile", ErrorCategory.InvalidOperation, null);553                                _cmdlet.ThrowTerminatingError(errorRecord);554                            }555 556                            ProcessCatalogFile(relativePath, currentMember.pwszReferenceTag, excludedPatterns, ref catalogHashes);557                        }558                    }559                } while (memberInfo != IntPtr.Zero);560            }561 562            return catalogHashes;563        }564 565        /// <summary>566        /// Process file in path for its relative paths.567        /// </summary>568        /// <param name="relativePath">Relative path of file found in catalog.</param>569        /// <param name="fileHash">Hash of file found in catalog.</param>570        /// <param name="excludedPatterns">Skip file from validation if it matches these patterns.</param>571        /// <param name="catalogHashes">Collection of hashes of catalog.</param>572        /// <returns>Void.</returns>573        internal static void ProcessCatalogFile(string relativePath, string fileHash, WildcardPattern[] excludedPatterns, ref Dictionary<string, string> catalogHashes)574        {575            // Found the attribute we are looking for576            _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.FoundFileHashInCatalogItem, relativePath, fileHash));577 578            // Only add the file for validation if it does not meet exclusion criteria579            if (!CheckExcludedCriteria((new FileInfo(relativePath)).Name, excludedPatterns))580            {581                // Add relativePath mapping to hashvalue for each file582                catalogHashes.Add(relativePath, fileHash);583            }584            else585            {586                // Verbose about skipping file from catalog587                _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.SkipValidationOfCatalogFile, relativePath));588            }589        }590        /// <summary>591        /// Process file in path for its relative paths.592        /// </summary>593        /// <param name="fileToHash">File to hash.</param>594        /// <param name="dirInfo">Directory information about file needed to calculate relative file path.</param>595        /// <param name="hashAlgorithm">Used to calculate Hash.</param>596        /// <param name="excludedPatterns">Skip file if it matches these patterns.</param>597        /// <param name="fileHashes">Collection of hashes of files.</param>598        /// <returns>Void.</returns>599        internal static void ProcessPathFile(FileInfo fileToHash, DirectoryInfo dirInfo, string hashAlgorithm, WildcardPattern[] excludedPatterns, ref Dictionary<string, string> fileHashes)600        {601            string relativePath = string.Empty;602            string exclude = string.Empty;603 604            if (dirInfo != null)605            {606                // Relative path of the file is the path inside the containing folder excluding folder Name607                relativePath = fileToHash.FullName.AsSpan(dirInfo.FullName.Length).TrimStart('\\').ToString();608                exclude = fileToHash.Name;609            }610            else611            {612                relativePath = fileToHash.Name;613                exclude = relativePath;614            }615 616            if (!CheckExcludedCriteria(exclude, excludedPatterns))617            {618                string fileHash = string.Empty;619 620                if (fileToHash.Length != 0)621                {622                    fileHash = CalculateFileHash(fileToHash.FullName, hashAlgorithm);623                }624 625                if (fileHashes.TryAdd(relativePath, fileHash))626                {627                    _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.FoundFileInPath, relativePath, fileHash));628                }629                else630                {631                    ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFilesRelativePath, relativePath)), "FoundDuplicateFilesRelativePath", ErrorCategory.InvalidOperation, null);632                    _cmdlet.ThrowTerminatingError(errorRecord);633                }634            }635            else636            {637                // Verbose about skipping file from path638                _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.SkipValidationOfPathFile, relativePath));639            }640        }641 642        /// <summary>643        /// Generate the hashes of all the files in given folder.644        /// </summary>645        /// <param name="folderPaths">Path to folder or File.</param>646        /// <param name="catalogFilePath">Catalog file path it should be skipped when calculating the hashes.</param>647        /// <param name="hashAlgorithm">Used to calculate Hash.</param>648        /// <param name="excludedPatterns"></param>649        /// <returns>Dictionary mapping file relative paths to hashes..</returns>650        internal static Dictionary<string, string> CalculateHashesFromPath(Collection<string> folderPaths, string catalogFilePath, string hashAlgorithm, WildcardPattern[] excludedPatterns)651        {652            // Create a HashTable of file Hashes653            Dictionary<string, string> fileHashes = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);654 655            foreach (string folderPath in folderPaths)656            {657                if (System.IO.Directory.Exists(folderPath))658                {659                    var directoryItems = Directory.EnumerateFiles(folderPath, "*.*", SearchOption.AllDirectories);660                    foreach (string fileItem in directoryItems)661                    {662                        // if its the catalog file we are validating we will skip it663                        if (string.Equals(fileItem, catalogFilePath, StringComparison.OrdinalIgnoreCase))664                            continue;665 666                        ProcessPathFile(new FileInfo(fileItem), new DirectoryInfo(folderPath), hashAlgorithm, excludedPatterns, ref fileHashes);667                    }668                }669                else if (System.IO.File.Exists(folderPath))670                {671                    ProcessPathFile(new FileInfo(folderPath), null, hashAlgorithm, excludedPatterns, ref fileHashes);672                }673            }674 675            return fileHashes;676        }677 678        /// <summary>679        /// Compare Dictionary objects.680        /// </summary>681        /// <param name="catalogItems">Hashes extracted from Catalog.</param>682        /// <param name="pathItems">Hashes created from folders path.</param>683        /// <returns>True if both collections are same.</returns>684        internal static bool CompareDictionaries(Dictionary<string, string> catalogItems, Dictionary<string, string> pathItems)685        {686            bool Status = true;687 688            List<string> relativePathsFromFolder = pathItems.Keys.ToList();689            List<string> relativePathsFromCatalog = catalogItems.Keys.ToList();690 691            // Find entries that are not in both lists. These should be empty lists for success692            // Hashes in Catalog should be exactly similar to the ones from folder693            List<string> relativePathsNotInFolder = relativePathsFromFolder.Except(relativePathsFromCatalog, StringComparer.CurrentCultureIgnoreCase).ToList();694            List<string> relativePathsNotInCatalog = relativePathsFromCatalog.Except(relativePathsFromFolder, StringComparer.CurrentCultureIgnoreCase).ToList();695 696            // Found extra hashes in Folder697            if ((relativePathsNotInFolder.Count != 0) || (relativePathsNotInCatalog.Count != 0))698            {699                Status = false;700            }701 702            foreach (KeyValuePair<string, string> item in catalogItems)703            {704                string catalogHashValue = (string)catalogItems[item.Key];705                if (pathItems.ContainsKey(item.Key))706                {707                    string folderHashValue = (string)pathItems[item.Key];708                    if (folderHashValue.Equals(catalogHashValue))709                    {710                        continue;711                    }712                    else713                    {714                        Status = false;715                    }716                }717            }718 719            return Status;720        }721        /// <summary>722        /// To Validate the Integrity of Catalog.723        /// </summary>724        /// <param name="catalogFolders">Folder for which catalog is created.</param>725        /// <param name="catalogFilePath">File Name of the Catalog.</param>726        /// <param name="excludedPatterns"></param>727        /// <param name="cmdlet">Instance of cmdlet calling this method.</param>728        /// <returns>Information about Catalog.</returns>729        internal static CatalogInformation ValidateCatalog(PSCmdlet cmdlet, Collection<string> catalogFolders, string catalogFilePath, WildcardPattern[] excludedPatterns)730        {731            _cmdlet = cmdlet;732            int catalogVersion = 0;733            Dictionary<string, string> catalogHashes = GetHashesFromCatalog(catalogFilePath, excludedPatterns, out catalogVersion);734            string hashAlgorithm = GetCatalogHashAlgorithm(catalogVersion);735 736            if (!string.IsNullOrEmpty(hashAlgorithm))737            {738                Dictionary<string, string> fileHashes = CalculateHashesFromPath(catalogFolders, catalogFilePath, hashAlgorithm, excludedPatterns);739                CatalogInformation catalog = new CatalogInformation();740                catalog.CatalogItems = catalogHashes;741                catalog.PathItems = fileHashes;742                bool status = CompareDictionaries(catalogHashes, fileHashes);743                if (status)744                {745                    catalog.Status = CatalogValidationStatus.Valid;746                }747                else748                {749                    catalog.Status = CatalogValidationStatus.ValidationFailed;750                }751 752                catalog.HashAlgorithm = hashAlgorithm;753                catalog.Signature = SignatureHelper.GetSignature(catalogFilePath, null);754                return catalog;755            }756 757            return null;758        }759 760        /// <summary>761        /// Check if file meets the skip validation criteria.762        /// </summary>763        /// <param name="filename"></param>764        /// <param name="excludedPatterns"></param>765        /// <returns>True if match is found else false.</returns>766        internal static bool CheckExcludedCriteria(string filename, WildcardPattern[] excludedPatterns)767        {768            if (excludedPatterns != null)769            {770                foreach (WildcardPattern patternItem in excludedPatterns)771                {772                    if (patternItem.IsMatch(filename))773                    {774                        return true;775                    }776                }777            }778 779            return false;780        }781        /// <summary>782        /// Call back when error is thrown by catalog API's.783        /// </summary>784        private static void ParseErrorCallback(uint dwErrorArea, uint dwLocalError, string pwszLine)785        {786            switch (dwErrorArea)787            {788                case NativeConstants.CRYPTCAT_E_AREA_HEADER:789                    break;790                case NativeConstants.CRYPTCAT_E_AREA_MEMBER:791                    break;792                case NativeConstants.CRYPTCAT_E_AREA_ATTRIBUTE:793                    break;794                default:795                    break;796            }797 798            switch (dwLocalError)799            {800                case NativeConstants.CRYPTCAT_E_CDF_MEMBER_FILE_PATH:801                    {802                        ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToFindFileNameOrPathForCatalogMember, pwszLine)), "UnableToFindFileNameOrPathForCatalogMember", ErrorCategory.InvalidOperation, null);803                        _cmdlet.ThrowTerminatingError(errorRecord);804                        break;805                    }806                case NativeConstants.CRYPTCAT_E_CDF_MEMBER_INDIRECTDATA:807                    {808                        ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, pwszLine)), "UnableToCreateFileHash", ErrorCategory.InvalidOperation, null);809                        _cmdlet.ThrowTerminatingError(errorRecord);810                        break;811                    }812                case NativeConstants.CRYPTCAT_E_CDF_MEMBER_FILENOTFOUND:813                    {814                        ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToFindFileToHash, pwszLine)), "UnableToFindFileToHash", ErrorCategory.InvalidOperation, null);815                        _cmdlet.ThrowTerminatingError(errorRecord);816                        break;817                    }818                case NativeConstants.CRYPTCAT_E_CDF_BAD_GUID_CONV:819                    break;820                case NativeConstants.CRYPTCAT_E_CDF_ATTR_TYPECOMBO:821                    break;822                case NativeConstants.CRYPTCAT_E_CDF_ATTR_TOOFEWVALUES:823                    break;824                case NativeConstants.CRYPTCAT_E_CDF_UNSUPPORTED:825                    break;826                case NativeConstants.CRYPTCAT_E_CDF_DUPLICATE:827                    {828                        ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFileMemberInCatalog, pwszLine)), "FoundDuplicateFileMemberInCatalog", ErrorCategory.InvalidOperation, null);829                        _cmdlet.ThrowTerminatingError(errorRecord);830                        break;831                    }832                case NativeConstants.CRYPTCAT_E_CDF_TAGNOTFOUND:833                    break;834                default:835                    break;836            }837        }838    }839}840 841#endif842