/// <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);
            }
        }
Exemplo n.º 2
0
        /// <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);
            }
        }
Exemplo n.º 3
0
        /// <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);
        }