private FileSignInfo TrackFile(string fullPath, ImmutableArray <byte> contentHash, bool isNested) { var fileSignInfo = ExtractSignInfo(fullPath, contentHash); var key = new SignedFileContentKey(contentHash, Path.GetFileName(fullPath)); if (_filesByContentKey.TryGetValue(key, out var existingSignInfo)) { // If we saw this file already we wouldn't call TrackFile unless this is a top-level file. Debug.Assert(!isNested); // Copy the signed content to the destination path. _filesToCopy.Add(new KeyValuePair <string, string>(existingSignInfo.FullPath, fullPath)); return(fileSignInfo); } if (FileSignInfo.IsZipContainer(fullPath)) { Debug.Assert(!_zipDataMap.ContainsKey(contentHash)); if (TryBuildZipData(fileSignInfo, out var zipData)) { _zipDataMap[contentHash] = zipData; } } _filesByContentKey.Add(key, fileSignInfo); if (fileSignInfo.SignInfo.ShouldSign || fileSignInfo.IsZipContainer()) { _filesToSign.Add(fileSignInfo); } return(fileSignInfo); }
internal static bool IsSignedContainer(string fullPath) { if (FileSignInfo.IsZipContainer(fullPath)) { bool signedContainer = false; using (var archive = new ZipArchive(File.OpenRead(fullPath), ZipArchiveMode.Read)) { foreach (ZipArchiveEntry entry in archive.Entries) { if (FileSignInfo.IsNupkg(fullPath) && VerifySignedNupkgByFileMarker(entry.FullName)) { if (!VerifySignedNupkgIntegrity(fullPath)) { return(false); } signedContainer = true; break; } else if (FileSignInfo.IsVsix(fullPath) && VerifySignedVSIXByFileMarker(entry.FullName)) { signedContainer = true; break; } } } if (!signedContainer) { return(false); } } return(true); }
private void VerifyAfterSign(FileSignInfo file) { if (file.IsPEFile()) { using (var stream = File.OpenRead(file.FullPath)) { if (!_signTool.VerifySignedPEFile(stream)) { _log.LogError($"Assembly {file.FullPath} is not signed properly"); } } } else if (file.IsPowerShellScript()) { if (!_signTool.VerifySignedPowerShellFile(file.FullPath)) { _log.LogError($"Powershell file {file.FullPath} does not have a signature mark."); } } else if (file.IsZipContainer()) { var zipData = _batchData.ZipDataMap[file.ContentHash]; bool signedContainer = false; using (var archive = new ZipArchive(File.OpenRead(file.FullPath), ZipArchiveMode.Read)) { foreach (ZipArchiveEntry entry in archive.Entries) { string relativeName = entry.FullName; if (!SkipZipContainerSignatureMarkerCheck) { if (file.IsNupkg() && _signTool.VerifySignedNugetFileMarker(relativeName)) { signedContainer = true; } else if (file.IsVsix() && _signTool.VerifySignedVSIXFileMarker(relativeName)) { signedContainer = true; } } var zipPart = zipData.FindNestedPart(relativeName); if (!zipPart.HasValue) { continue; } VerifyAfterSign(zipPart.Value.FileSignInfo); } } if (!SkipZipContainerSignatureMarkerCheck && (file.IsNupkg() || file.IsVsix()) && !signedContainer) { _log.LogError($"Container {file.FullPath} does not have signature marker."); } } }
private FileSignInfo TrackFile(string fullPath, ImmutableArray <byte> contentHash, bool isNested, bool forceRepack = false) { _log.LogMessage($"Tracking file '{fullPath}' isNested={isNested}"); var fileSignInfo = ExtractSignInfo(fullPath, contentHash, forceRepack); var key = new SignedFileContentKey(contentHash, Path.GetFileName(fullPath)); if (_filesByContentKey.TryGetValue(key, out var existingSignInfo)) { // If we saw this file already we wouldn't call TrackFile unless this is a top-level file. Debug.Assert(!isNested); // Copy the signed content to the destination path. _filesToCopy.Add(new KeyValuePair <string, string>(existingSignInfo.FullPath, fullPath)); return(fileSignInfo); } if (FileSignInfo.IsZipContainer(fullPath)) { if (_zipDataMap.ContainsKey(contentHash)) { _log.LogError($"File '{fullPath}' has the same content hash as '{_zipDataMap[contentHash].FileSignInfo.FullPath}'. The incorrect file will be written."); } if (TryBuildZipData(fileSignInfo, out var zipData)) { _zipDataMap[contentHash] = zipData; } } _log.LogMessage(MessageImportance.Low, $"Caching file {key.FileName} {key.StringHash}"); _filesByContentKey.Add(key, fileSignInfo); if (fileSignInfo.SignInfo.ShouldSign || fileSignInfo.ForceRepack || fileSignInfo.IsZipContainer()) { _filesToSign.Add(fileSignInfo); } return(fileSignInfo); }
/// <summary> /// Build up the <see cref="ZipData"/> instance for a given zip container. This will also report any consistency /// errors found when examining the zip archive. /// </summary> private bool TryBuildZipData(FileSignInfo zipFileSignInfo, out ZipData zipData) { Debug.Assert(zipFileSignInfo.IsZipContainer()); try { using (var archive = new ZipArchive(File.OpenRead(zipFileSignInfo.FullPath), ZipArchiveMode.Read)) { var nestedParts = new List <ZipPart>(); foreach (ZipArchiveEntry entry in archive.Entries) { string relativePath = entry.FullName; // `entry` might be just a pointer to a folder. We skip those. if (relativePath.EndsWith("/") && entry.Name == "") { continue; } ImmutableArray <byte> contentHash; using (var stream = entry.Open()) { contentHash = ContentUtil.GetContentHash(stream); } var fileUniqueKey = new SignedFileContentKey(contentHash, relativePath); if (!_whichPackagesTheFileIsIn.TryGetValue(fileUniqueKey, out var packages)) { packages = new HashSet <string>(); } packages.Add(zipFileSignInfo.FileName); _whichPackagesTheFileIsIn[fileUniqueKey] = packages; // if we already encountered file that hash the same content we can reuse its signed version when repackaging the container. var fileName = Path.GetFileName(relativePath); if (!_filesByContentKey.TryGetValue(new SignedFileContentKey(contentHash, fileName), out var fileSignInfo)) { string extractPathRoot = _useHashInExtractionPath ? ContentUtil.HashToString(contentHash) : _filesByContentKey.Count().ToString(); string tempPath = Path.Combine(_pathToContainerUnpackingDirectory, extractPathRoot, relativePath); _log.LogMessage($"Extracting file '{fileName}' from '{zipFileSignInfo.FullPath}' to '{tempPath}'."); Directory.CreateDirectory(Path.GetDirectoryName(tempPath)); using (var stream = entry.Open()) using (var tempFileStream = File.OpenWrite(tempPath)) { stream.CopyTo(tempFileStream); } fileSignInfo = TrackFile(tempPath, contentHash, isNested: true); } if (fileSignInfo.SignInfo.ShouldSign || fileSignInfo.ForceRepack) { nestedParts.Add(new ZipPart(relativePath, fileSignInfo)); } } zipData = new ZipData(zipFileSignInfo, nestedParts.ToImmutableArray()); return(true); } } catch (Exception e) { _log.LogErrorFromException(e); zipData = null; return(false); } }
/// <summary> /// Build up the <see cref="ZipData"/> instance for a given zip container. This will also report any consistency /// errors found when examining the zip archive. /// </summary> private bool TryBuildZipData(FileSignInfo zipFileSignInfo, out ZipData zipData, string alternativeArchivePath = null) { string archivePath = zipFileSignInfo.FullPath; if (alternativeArchivePath != null) { archivePath = alternativeArchivePath; Debug.Assert(Path.GetExtension(archivePath) == ".zip"); } else { Debug.Assert(zipFileSignInfo.IsZipContainer()); } try { using (var archive = new ZipArchive(File.OpenRead(archivePath), ZipArchiveMode.Read)) { var nestedParts = new Dictionary <string, ZipPart>(); foreach (ZipArchiveEntry entry in archive.Entries) { string relativePath = entry.FullName; // `entry` might be just a pointer to a folder. We skip those. if (relativePath.EndsWith("/") && entry.Name == "") { continue; } using (var entryStream = entry.Open()) using (MemoryStream entryMemoryStream = new MemoryStream((int)entry.Length)) { entryStream.CopyTo(entryMemoryStream); entryMemoryStream.Position = 0; ImmutableArray <byte> contentHash = ContentUtil.GetContentHash(entryMemoryStream); var fileUniqueKey = new SignedFileContentKey(contentHash, Path.GetFileName(relativePath)); if (!_whichPackagesTheFileIsIn.TryGetValue(fileUniqueKey, out var packages)) { packages = new HashSet <string>(); } packages.Add(Path.GetFileName(archivePath)); _whichPackagesTheFileIsIn[fileUniqueKey] = packages; // if we already encountered file that has the same content we can reuse its signed version when repackaging the container. var fileName = Path.GetFileName(relativePath); if (!_filesByContentKey.TryGetValue(fileUniqueKey, out var fileSignInfo)) { string extractPathRoot = _useHashInExtractionPath ? fileUniqueKey.StringHash : _filesByContentKey.Count().ToString(); string tempPath = Path.Combine(_pathToContainerUnpackingDirectory, extractPathRoot, relativePath); _log.LogMessage($"Extracting file '{fileName}' from '{archivePath}' to '{tempPath}'."); Directory.CreateDirectory(Path.GetDirectoryName(tempPath)); entryMemoryStream.Position = 0; using (var tempFileStream = File.OpenWrite(tempPath)) { entryMemoryStream.CopyTo(tempFileStream); } _hashToCollisionIdMap.TryGetValue(fileUniqueKey, out string collisionPriorityId); PathWithHash nestedFile = new PathWithHash(tempPath, contentHash); fileSignInfo = TrackFile(nestedFile, zipFileSignInfo.File, collisionPriorityId); } if (fileSignInfo.ShouldTrack) { nestedParts.Add(relativePath, new ZipPart(relativePath, fileSignInfo)); } } } zipData = new ZipData(zipFileSignInfo, nestedParts.ToImmutableDictionary()); return(true); } } catch (Exception e) { _log.LogErrorFromException(e); zipData = null; return(false); } }
/// <summary> /// Build up the <see cref="ZipData"/> instance for a given zip container. This will also report any consistency /// errors found when examining the zip archive. /// </summary> private bool TryBuildZipData(FileSignInfo zipFileSignInfo, out ZipData zipData) { Debug.Assert(zipFileSignInfo.IsZipContainer()); try { using (var archive = new ZipArchive(File.OpenRead(zipFileSignInfo.FullPath), ZipArchiveMode.Read)) { var nestedParts = new List <ZipPart>(); foreach (ZipArchiveEntry entry in archive.Entries) { string relativePath = entry.FullName; // `entry` might be just a pointer to a folder. We skip those. if (relativePath.EndsWith("/") && entry.Name == "") { continue; } ImmutableArray <byte> contentHash; using (var stream = entry.Open()) { contentHash = ContentUtil.GetContentHash(stream); } // if we already encountered file that hash the same content we can reuse its signed version when repackaging the container. string fileName = Path.GetFileName(relativePath); if (!_filesByContentKey.TryGetValue(new SignedFileContentKey(contentHash, fileName), out var fileSignInfo)) { string tempDir = Path.Combine(_pathToContainerUnpackingDirectory, ContentUtil.HashToString(contentHash)); string tempPath = Path.Combine(tempDir, Path.GetFileName(relativePath)); Directory.CreateDirectory(tempDir); using (var stream = entry.Open()) using (var tempFileStream = File.OpenWrite(tempPath)) { stream.CopyTo(tempFileStream); } fileSignInfo = TrackFile(tempPath, contentHash, isNested: true); } if (fileSignInfo.SignInfo.ShouldSign) { nestedParts.Add(new ZipPart(relativePath, fileSignInfo)); } } zipData = new ZipData(zipFileSignInfo, nestedParts.ToImmutableArray()); return(true); } } catch (Exception e) { _log.LogErrorFromException(e); zipData = null; return(false); } }
/// <summary> /// Build up the <see cref="ZipData"/> instance for a given zip container. This will also report any consistency /// errors found when examining the zip archive. /// </summary> private bool TryBuildZipData(FileSignInfo zipFileSignInfo, out ZipData zipData) { Debug.Assert(zipFileSignInfo.IsZipContainer()); try { using (var archive = new ZipArchive(File.OpenRead(zipFileSignInfo.FullPath), ZipArchiveMode.Read)) { var nestedParts = new List <ZipPart>(); foreach (ZipArchiveEntry entry in archive.Entries) { string relativePath = entry.FullName; string extension = Path.GetExtension(relativePath); if (!FileSignInfo.IsZipContainer(relativePath) && (!_fileExtensionSignInfo.TryGetValue(extension, out var extensionSignInfo) || !extensionSignInfo.ShouldSign)) { var reason = extensionSignInfo.ShouldIgnore ? "configuration tells to ignore this extension" : "its extension isn't on recognizable signing extension list"; _log.LogMessage($"Ignoring this file because {reason} : {relativePath}"); continue; } ImmutableArray <byte> contentHash; using (var stream = entry.Open()) { contentHash = ContentUtil.GetContentHash(stream); } // if we already encountered file that hash the same content we can reuse its signed version when repackaging the container. string fileName = Path.GetFileName(relativePath); if (!_filesByContentKey.TryGetValue(new SignedFileContentKey(contentHash, fileName), out var fileSignInfo)) { string tempDir = Path.Combine(_pathToContainerUnpackingDirectory, ContentUtil.HashToString(contentHash)); string tempPath = Path.Combine(tempDir, Path.GetFileName(relativePath)); Directory.CreateDirectory(tempDir); using (var stream = entry.Open()) using (var tempFileStream = File.OpenWrite(tempPath)) { stream.CopyTo(tempFileStream); } fileSignInfo = TrackFile(tempPath, contentHash, isNested: true); } if (fileSignInfo.SignInfo.ShouldSign) { nestedParts.Add(new ZipPart(relativePath, fileSignInfo)); } } zipData = new ZipData(zipFileSignInfo, nestedParts.ToImmutableArray()); return(true); } } catch (Exception e) { _log.LogErrorFromException(e); zipData = null; return(false); } }