/// <summary> /// Build up a table of checksum to <see cref="FileName"/> instance map. This will report errors if it /// is unable to read any of the files off of disk. /// </summary> private ContentMap BuildContentMap(TextWriter textWriter, ref bool allGood) { var contentMap = new ContentMap(); foreach (var fileName in _batchData.FileNames) { try { var checksum = _contentUtil.GetChecksum(fileName.FullPath); contentMap.Add(fileName, checksum); } catch (Exception ex) { if (!File.Exists(fileName.FullPath)) { textWriter.WriteLine($"Did not find {fileName} at {fileName.FullPath}"); } else { textWriter.WriteLine($"Unable to read content of {fileName.FullPath}: {ex.Message}"); } allGood = false; } } return(contentMap); }
/// <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 ZipData BuildZipData(FileName zipFileName, ContentMap contentMap, TextWriter textWriter, ref bool allGood) { Debug.Assert(zipFileName.IsZipContainer); var nestedExternalBinaries = new List <string>(); var nestedParts = new List <ZipPart>(); using (var package = Package.Open(zipFileName.FullPath, FileMode.Open, FileAccess.Read)) { foreach (var part in package.GetParts()) { var relativeName = GetPartRelativeFileName(part); var name = Path.GetFileName(relativeName); if (!IsZipContainer(name) && !IsAssembly(name)) { continue; } if (_batchData.ExternalFileNames.Contains(name)) { nestedExternalBinaries.Add(name); continue; } if (!_batchData.FileNames.Any(x => FilePathComparer.Equals(x.Name, name))) { allGood = false; textWriter.WriteLine($"signtool : error : Zip Container '{zipFileName}' has part '{relativeName}' which is not listed in the sign or external list"); continue; } // This represents a binary that we need to sign. Ensure the content in the VSIX is the same as the // content in the binaries directory by doing a checksum match. using (var stream = part.GetStream()) { string checksum = _contentUtil.GetChecksum(stream); if (!contentMap.TryGetFileName(checksum, out var checksumName)) { allGood = false; textWriter.WriteLine($"signtool : error : {zipFileName} has part {relativeName} which does not match the content in the binaries directory"); continue; } if (!FilePathComparer.Equals(checksumName.Name, name)) { allGood = false; textWriter.WriteLine($"signtool : error : {zipFileName} has part {relativeName} with a different name in the binaries directory: {checksumName}"); continue; } nestedParts.Add(new ZipPart(relativeName, checksumName, checksum)); } } } return(new ZipData(zipFileName, nestedParts.ToImmutableArray(), nestedExternalBinaries.ToImmutableArray())); }
private Dictionary <FileName, ZipData> BuildAllZipData(ContentMap contentMap, TextWriter textWriter, ref bool allGood) { var zipDataMap = new Dictionary <FileName, ZipData>(); foreach (var zipName in _batchData.FileNames.Where(x => x.IsZipContainer)) { var data = BuildZipData(zipName, contentMap, textWriter, ref allGood); zipDataMap[zipName] = data; } return(zipDataMap); }
/// <summary> /// Build up a table of checksum to <see cref="FileName"/> instance map. This will report errors if it /// is unable to read any of the files off of disk. /// </summary> private ContentMap BuildContentMap(TextWriter textWriter, ref bool allGood) { var contentMap = new ContentMap(); foreach (var fileName in _batchData.FileNames) { try { if (string.IsNullOrEmpty(fileName.SHA256Hash)) { var checksum = _contentUtil.GetChecksum(fileName.FullPath); contentMap.Add(fileName, checksum); } else { if (File.Exists(fileName.FullPath)) { contentMap.Add(fileName, fileName.SHA256Hash); } else { throw new FileNotFoundException(); } } } catch (Exception ex) { if (!File.Exists(fileName.FullPath)) { textWriter.WriteLine($"signtool : error : Did not find {fileName} at {fileName.FullPath}"); } else { textWriter.WriteLine($"signtool : error : Unable to read content of {fileName.FullPath}: {ex.Message}"); } allGood = false; } } return(contentMap); }
private bool GenerateOrchestrationManifest(TextWriter textWriter, BatchSignInput batchData, ContentMap contentMap, string outputPath) { textWriter.WriteLine($"Generating orchestration file manifest into {outputPath}"); OrchestratedFileJson fileJsonWithInfo = new OrchestratedFileJson { ExcludeList = _batchData.ExternalFileNames.ToArray() ?? Array.Empty <string>() }; var distinctSigningCombos = batchData.FileSignInfoMap.Values.GroupBy(v => new { v.Certificate, v.StrongName }); List <OrchestratedFileSignData> newList = new List <OrchestratedFileSignData>(); foreach (var combinationToSign in distinctSigningCombos) { var filesInThisGroup = combinationToSign.Select(c => new FileSignDataEntry() { FilePath = c.FileName.RelativePath, SHA256Hash = contentMap.GetChecksum(c.FileName), PublishToFeedUrl = batchData.PublishUri }); newList.Add(new OrchestratedFileSignData() { Certificate = combinationToSign.Key.Certificate, StrongName = combinationToSign.Key.StrongName, FileList = filesInThisGroup.ToArray() }); } fileJsonWithInfo.SignList = newList.ToArray(); fileJsonWithInfo.Kind = "orchestration"; using (StreamWriter file = File.CreateText(outputPath)) { file.Write(JsonConvert.SerializeObject(fileJsonWithInfo, Formatting.Indented)); } return(true); }
/// <summary> /// Actually sign all of the described files. /// </summary> private bool SignFiles(ContentMap contentMap, Dictionary <FileName, ZipData> zipDataMap, TextWriter textWriter) { // Generate the list of signed files in a deterministic order. Makes it easier to track down // bugs if repeated runs use the same ordering. var toSignList = _batchData.FileNames.ToList(); var round = 0; var signedSet = new HashSet <FileName>(); void signFiles(IEnumerable <FileName> files) { textWriter.WriteLine($"Signing Round {round}"); foreach (var name in files) { textWriter.WriteLine($"\t{name}"); } _signTool.Sign(round, files.Select(x => _batchData.FileSignInfoMap[x]).Where(x => !x.IsEmpty), textWriter); } void repackFiles(IEnumerable <FileName> files) { var any = false; foreach (var file in files) { if (file.IsZipContainer) { if (!any) { textWriter.WriteLine("Repacking"); any = true; } textWriter.WriteLine($"\t{file}"); Repack(zipDataMap[file]); } } } // Is this file ready to be signed? That is are all of the items that it depends on already // signed? bool isReadyToSign(FileName fileName) { if (!fileName.IsZipContainer) { return(true); } var zipData = zipDataMap[fileName]; return(zipData.NestedParts.All(x => signedSet.Contains(x.FileName))); } // Extract the next set of files that should be signed. This is the set of files for which all of the // dependencies have been signed. List <FileName> extractNextGroup() { var list = new List <FileName>(); var i = 0; while (i < toSignList.Count) { var current = toSignList[i]; if (isReadyToSign(current)) { list.Add(current); toSignList.RemoveAt(i); } else { i++; } } return(list); } try { while (toSignList.Count > 0) { var list = extractNextGroup(); if (list.Count == 0) { throw new Exception("No progress made on signing which indicates a bug"); } repackFiles(list); signFiles(list); round++; list.ForEach(x => signedSet.Add(x)); } return(true); } catch (Exception ex) { textWriter.WriteLine($"Signing failed: {ex.Message}"); return(false); } }