/// <summary> /// Asserts that the SignedPackageArchiveMetadata contains only one Signature file entry. /// Updates SignedPackageArchiveMetadata.SignatureCentralDirectoryHeaderIndex with the index of the signature central directory header. /// Throws SignatureException if less or more entries are found. /// </summary> /// <param name="metadata">SignedPackageArchiveMetadata to be checked for signature entry.</param> internal static void AssertExactlyOnePrimarySignatureAndUpdateMetadata(SignedPackageArchiveMetadata metadata) { // Get missing metadata for central directory records var hasFoundSignature = false; var centralDirectoryRecords = metadata.CentralDirectoryHeaders; var centralDirectoryRecordsCount = centralDirectoryRecords.Count; for (var centralDirectoryRecordIndex = 0; centralDirectoryRecordIndex < centralDirectoryRecordsCount; centralDirectoryRecordIndex++) { var record = centralDirectoryRecords[centralDirectoryRecordIndex]; if (StringComparer.Ordinal.Equals(record.Filename, _signingSpecification.SignaturePath)) { if (hasFoundSignature) { throw new SignatureException(Strings.Error_NotOnePrimarySignature); } metadata.SignatureCentralDirectoryHeaderIndex = centralDirectoryRecordIndex; hasFoundSignature = true; } } if (!hasFoundSignature) { throw new SignatureException(Strings.Error_NotOnePrimarySignature); } }
/// <summary> /// Asserts that the SignedPackageArchiveMetadata contains only one Signature file entry. /// Updates SignedPackageArchiveMetadata.SignatureCentralDirectoryHeaderIndex with the index of the signature central directory header. /// Throws SignatureException if less or more entries are found. /// </summary> /// <param name="metadata">SignedPackageArchiveMetadata to be checked for signature entry.</param> internal static void AssertExactlyOnePrimarySignatureAndUpdateMetadata(SignedPackageArchiveMetadata metadata) { // Get missing metadata for central directory records var hasFoundSignature = false; var centralDirectoryRecords = metadata.CentralDirectoryHeaders; var centralDirectoryRecordsCount = centralDirectoryRecords.Count; for (var centralDirectoryRecordIndex = 0; centralDirectoryRecordIndex < centralDirectoryRecordsCount; centralDirectoryRecordIndex++) { var record = centralDirectoryRecords[centralDirectoryRecordIndex]; if (record.IsPackageSignatureFile) { if (hasFoundSignature) { throw new SignatureException(NuGetLogCode.NU3009, Strings.Error_NotOnePrimarySignature); } metadata.SignatureCentralDirectoryHeaderIndex = centralDirectoryRecordIndex; hasFoundSignature = true; } } if (!hasFoundSignature) { throw new SignatureException(NuGetLogCode.NU3009, Strings.Error_NotOnePrimarySignature); } }
/// <summary> /// Read ZIP's offsets and positions of offsets. /// </summary> /// <param name="reader">binary reader to zip archive</param> /// <returns>metadata with offsets and positions for entries</returns> public static SignedPackageArchiveMetadata ReadSignedArchiveMetadata(BinaryReader reader) { if (reader == null) { throw new ArgumentNullException(nameof(reader)); } var metadata = new SignedPackageArchiveMetadata() { StartOfFileHeaders = reader.BaseStream.Length }; var endOfCentralDirectoryRecord = EndOfCentralDirectoryRecord.Read(reader); metadata.EndOfCentralDirectoryRecordPosition = endOfCentralDirectoryRecord.OffsetFromStart; reader.BaseStream.Seek(endOfCentralDirectoryRecord.OffsetOfStartOfCentralDirectory, SeekOrigin.Begin); // Read central directory records var centralDirectoryRecords = new List <CentralDirectoryHeaderMetadata>(); CentralDirectoryHeader header; while (CentralDirectoryHeader.TryRead(reader, out header)) { if (header.RelativeOffsetOfLocalHeader < metadata.StartOfFileHeaders) { metadata.StartOfFileHeaders = header.RelativeOffsetOfLocalHeader; } var isPackageSignatureFile = SignedPackageArchiveUtility.IsPackageSignatureFileEntry( header.FileName, header.GeneralPurposeBitFlag); var centralDirectoryMetadata = new CentralDirectoryHeaderMetadata() { IsPackageSignatureFile = isPackageSignatureFile, HeaderSize = header.GetSizeInBytes(), OffsetToFileHeader = header.RelativeOffsetOfLocalHeader, Position = header.OffsetFromStart }; centralDirectoryRecords.Add(centralDirectoryMetadata); } if (centralDirectoryRecords.Count == 0) { throw new InvalidDataException(Strings.ErrorInvalidPackageArchive); } var lastCentralDirectoryRecord = centralDirectoryRecords.Last(); metadata.EndOfCentralDirectory = lastCentralDirectoryRecord.Position + lastCentralDirectoryRecord.HeaderSize; metadata.CentralDirectoryHeaders = centralDirectoryRecords; UpdateSignedPackageArchiveMetadata(reader, metadata); return(metadata); }
/// <summary> /// Updates the SignedPackageArchiveMetadata.CentralDirectoryHeaders by updating IndexInHeaders and FileEntryTotalSize. /// Updates the SignedPackageArchiveMetadata.EndOfFileHeaders. /// </summary> /// <param name="reader">Binary reader to zip archive.</param> /// <param name="metadata">SignedPackageArchiveMetadata to be updated.</param> public static void UpdateSignedPackageArchiveMetadata( BinaryReader reader, SignedPackageArchiveMetadata metadata) { // Get missing metadata for central directory records var centralDirectoryRecords = metadata.CentralDirectoryHeaders; var centralDirectoryRecordsCount = centralDirectoryRecords.Count; var endOfAllFileHeaders = 0L; for (var centralDirectoryRecordIndex = 0; centralDirectoryRecordIndex < centralDirectoryRecordsCount; centralDirectoryRecordIndex++) { var record = centralDirectoryRecords[centralDirectoryRecordIndex]; if (StringComparer.Ordinal.Equals(record.Filename, _signingSpecification.SignaturePath)) { metadata.SignatureCentralDirectoryHeaderIndex = centralDirectoryRecordIndex; } // Go to local file header reader.BaseStream.Seek(offset: record.OffsetToFileHeader, origin: SeekOrigin.Begin); // Validate file header signature var fileHeaderSignature = reader.ReadUInt32(); if (fileHeaderSignature != LocalFileHeader.Signature) { throw new InvalidDataException(Strings.ErrorInvalidPackageArchive); } // The total size of file entry is from the start of the file header until // the start of the next file header (or the start of the first central directory header) try { SeekReaderForwardToMatchByteSignature(reader, BitConverter.GetBytes(LocalFileHeader.Signature)); } // No local File header found (entry must be the last entry), search for the start of the first central directory catch { SeekReaderForwardToMatchByteSignature(reader, BitConverter.GetBytes(CentralDirectoryHeader.Signature)); } record.IndexInHeaders = centralDirectoryRecordIndex; record.FileEntryTotalSize = reader.BaseStream.Position - record.OffsetToFileHeader; var endOfFileHeader = record.FileEntryTotalSize + record.OffsetToFileHeader; if (endOfFileHeader > endOfAllFileHeaders) { endOfAllFileHeaders = endOfFileHeader; } } metadata.EndOfFileHeaders = endOfAllFileHeaders; }
/// <summary> /// Asserts the validity of central directory header and local file header for the package signature file entry. /// </summary> /// <param name="reader">BinaryReader on the package.</param> /// <param name="metadata">Metadata for the package signature file's central directory header.</param> /// <exception cref="SignatureException">Thrown if either header is invalid.</exception> private static void AssertSignatureEntryMetadata(BinaryReader reader, SignedPackageArchiveMetadata metadata) { var signatureCentralDirectoryHeader = metadata.GetPackageSignatureFileCentralDirectoryHeaderMetadata(); // Move to central directory header and skip header signature (4 bytes) and version fields (2 entries of 2 bytes each) reader.BaseStream.Seek(offset: signatureCentralDirectoryHeader.Position + 8L, origin: SeekOrigin.Begin); // check central directory file header AssertSignatureEntryCommonHeaderFields( reader, signatureCentralDirectoryHeader, Strings.InvalidPackageSignatureFileEntry, Strings.InvalidPackageSignatureFileEntryCentralDirectoryHeader); // Skip file name length (2 bytes), extra field length (2 bytes), file comment length (2 bytes), // disk number start (2 bytes), and internal file attributes (2 bytes) reader.BaseStream.Seek(offset: 10, origin: SeekOrigin.Current); var externalFileAttributes = reader.ReadUInt32(); AssertValue( expectedValue: 0U, actualValue: externalFileAttributes, errorCode: NuGetLogCode.NU3005, errorMessagePrefix: Strings.InvalidPackageSignatureFileEntry, errorMessageSuffix: Strings.InvalidPackageSignatureFileEntryCentralDirectoryHeader, fieldName: "external file attributes"); // Move to local file header and skip header signature (4 bytes) and version field (2 bytes) reader.BaseStream.Seek(offset: signatureCentralDirectoryHeader.OffsetToLocalFileHeader + 6L, origin: SeekOrigin.Begin); // check local file header AssertSignatureEntryCommonHeaderFields( reader, signatureCentralDirectoryHeader, Strings.InvalidPackageSignatureFileEntry, Strings.InvalidPackageSignatureFileEntryLocalFileHeader); }
private static List <CentralDirectoryHeaderMetadata> RemoveSignatureAndOrderByOffset(SignedPackageArchiveMetadata metadata) { // Remove signature cdr var centralDirectoryRecordsList = metadata.CentralDirectoryHeaders.Where((v, i) => i != metadata.SignatureCentralDirectoryHeaderIndex).ToList(); // Sort by order of file entries centralDirectoryRecordsList.Sort((x, y) => x.OffsetToLocalFileHeader.CompareTo(y.OffsetToLocalFileHeader)); // Update offsets with removed signature var previousRecordFileEntryEnd = 0L; foreach (var centralDirectoryRecord in centralDirectoryRecordsList) { centralDirectoryRecord.ChangeInOffset = previousRecordFileEntryEnd - centralDirectoryRecord.OffsetToLocalFileHeader; previousRecordFileEntryEnd = centralDirectoryRecord.OffsetToLocalFileHeader + centralDirectoryRecord.FileEntryTotalSize + centralDirectoryRecord.ChangeInOffset; } return(centralDirectoryRecordsList); }
/// <summary> /// Read ZIP's offsets and positions of offsets. /// </summary> /// <param name="reader">binary reader to zip archive</param> /// <returns>metadata with offsets and positions for entries</returns> public static SignedPackageArchiveMetadata ReadSignedArchiveMetadata(BinaryReader reader) { if (reader == null) { throw new ArgumentNullException(nameof(reader)); } var metadata = new SignedPackageArchiveMetadata() { StartOfLocalFileHeaders = reader.BaseStream.Length }; var endOfCentralDirectoryRecord = EndOfCentralDirectoryRecord.Read(reader); var endOfCentralDirectoryRecordPosition = endOfCentralDirectoryRecord.OffsetFromStart; reader.BaseStream.Seek(endOfCentralDirectoryRecord.OffsetOfStartOfCentralDirectory, SeekOrigin.Begin); var centralDirectoryRecords = new List <CentralDirectoryHeaderMetadata>(); var packageSignatureFileMetadataIndex = -1; var index = 0; while (CentralDirectoryHeader.TryRead(reader, out var header)) { metadata.StartOfLocalFileHeaders = Math.Min(metadata.StartOfLocalFileHeaders, header.RelativeOffsetOfLocalHeader); var isPackageSignatureFile = SignedPackageArchiveUtility.IsPackageSignatureFileEntry( header.FileName, header.GeneralPurposeBitFlag); if (isPackageSignatureFile) { if (packageSignatureFileMetadataIndex != -1) { throw new SignatureException(NuGetLogCode.NU3005, Strings.MultiplePackageSignatureFiles); } packageSignatureFileMetadataIndex = index; } var centralDirectoryMetadata = new CentralDirectoryHeaderMetadata() { Position = header.OffsetFromStart, OffsetToLocalFileHeader = header.RelativeOffsetOfLocalHeader, IsPackageSignatureFile = isPackageSignatureFile, HeaderSize = header.GetSizeInBytes(), IndexInHeaders = index }; centralDirectoryRecords.Add(centralDirectoryMetadata); ++index; } if (centralDirectoryRecords.Count == 0) { throw new InvalidDataException(Strings.ErrorInvalidPackageArchive); } if (packageSignatureFileMetadataIndex == -1) { throw new SignatureException(NuGetLogCode.NU3005, Strings.NoPackageSignatureFile); } var lastCentralDirectoryRecord = centralDirectoryRecords.Last(); var endOfCentralDirectoryPosition = lastCentralDirectoryRecord.Position + lastCentralDirectoryRecord.HeaderSize; var endOfLocalFileHeadersPosition = GetEndOfLocalFileHeadersPosition(reader, centralDirectoryRecords); metadata.EndOfCentralDirectory = lastCentralDirectoryRecord.Position + lastCentralDirectoryRecord.HeaderSize; metadata.CentralDirectoryHeaders = centralDirectoryRecords; metadata.EndOfLocalFileHeaders = endOfLocalFileHeadersPosition; metadata.SignatureCentralDirectoryHeaderIndex = packageSignatureFileMetadataIndex; AssertSignatureEntryMetadata(reader, metadata); return(metadata); }