internal static void HashUInt32(Sha512HashFunction hashFunc, uint value)
 {
     byte[] array = BitConverter.GetBytes(value);
     if (!BitConverter.IsLittleEndian)
     {
         Array.Reverse(array);
     }
     SignedPackageArchiveIOUtility.HashBytes(hashFunc, array);
 }
 internal static void HashUInt32(HashAlgorithm hashAlgorithm, uint value)
 {
     byte[] array = BitConverter.GetBytes(value);
     if (!BitConverter.IsLittleEndian)
     {
         Array.Reverse(array);
     }
     SignedPackageArchiveIOUtility.HashBytes(hashAlgorithm, array);
 }
        /// <summary>
        /// Opens a read-only stream for the package signature file.
        /// </summary>
        /// <remarks>Callers should first verify that a package is signed before calling this method.</remarks>
        /// <param name="reader">A binary reader for a signed package.</param>
        /// <returns>A readable stream.</returns>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="reader" /> is <c>null</c>.</exception>
        /// <exception cref="SignatureException">Thrown if a package signature file is invalid or missing.</exception>
        public static Stream OpenPackageSignatureFileStream(BinaryReader reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            var metadata = SignedPackageArchiveIOUtility.ReadSignedArchiveMetadata(reader);
            var signatureCentralDirectoryHeader = metadata.GetPackageSignatureFileCentralDirectoryHeaderMetadata();

            return(GetPackageSignatureFile(reader, signatureCentralDirectoryHeader));
        }
        internal static void UnsignZip(BinaryReader reader, BinaryWriter writer)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

            SignedPackageArchiveIOUtility.RemoveSignature(reader, writer);
        }
Ejemplo n.º 5
0
        internal static EndOfCentralDirectoryRecord Read(BinaryReader reader)
        {
            reader.BaseStream.Seek(offset: -22, origin: SeekOrigin.End);

            SignedPackageArchiveIOUtility.SeekReaderBackwardToMatchByteSignature(reader, BitConverter.GetBytes(Signature));

            var header = new EndOfCentralDirectoryRecord();

            header.OffsetFromStart = reader.BaseStream.Position;

            reader.ReadUInt32(); // Read and discard the byte signature.

            header.NumberOfThisDisk = reader.ReadUInt16();
            header.NumberOfTheDiskWithTheStartOfTheCentralDirectory = reader.ReadUInt16();
            header.CountOfEntriesInCentralDirectoryOnThisDisk       = reader.ReadUInt16();
            header.CountOfEntriesInCentralDirectory = reader.ReadUInt16();
            header.SizeOfCentralDirectory           = reader.ReadUInt32();
            header.OffsetOfStartOfCentralDirectory  = reader.ReadUInt32();
            header.FileCommentLength = reader.ReadUInt16();
            header.FileComment       = reader.ReadBytes(header.FileCommentLength);

            return(header);
        }
        internal static string GetPackageContentHash(BinaryReader reader)
        {
            using (var hashFunc = new Sha512HashFunction())
            {
                // skip validating signature entry since we're just trying to get the content hash here instead of
                // verifying signature entry.
                var metadata = SignedPackageArchiveIOUtility.ReadSignedArchiveMetadata(reader, validateSignatureEntry: false);
                var signatureCentralDirectoryHeader         = metadata.GetPackageSignatureFileCentralDirectoryHeaderMetadata();
                var centralDirectoryRecordsWithoutSignature = RemoveSignatureAndOrderByOffset(metadata);

                // Read and hash from the start of the archive to the start of the file headers
                reader.BaseStream.Seek(offset: 0, origin: SeekOrigin.Begin);
                SignedPackageArchiveIOUtility.ReadAndHashUntilPosition(reader, hashFunc, metadata.StartOfLocalFileHeaders);

                // Read and hash file headers
                foreach (var record in centralDirectoryRecordsWithoutSignature)
                {
                    reader.BaseStream.Seek(offset: record.OffsetToLocalFileHeader, origin: SeekOrigin.Begin);
                    SignedPackageArchiveIOUtility.ReadAndHashUntilPosition(reader, hashFunc, record.OffsetToLocalFileHeader + record.FileEntryTotalSize);
                }

                // Order central directory records by their position
                centralDirectoryRecordsWithoutSignature.Sort((x, y) => x.Position.CompareTo(y.Position));

                // Update offset of any central directory record that has a file entry after signature
                foreach (var record in centralDirectoryRecordsWithoutSignature)
                {
                    reader.BaseStream.Seek(offset: record.Position, origin: SeekOrigin.Begin);
                    // Hash from the start of the central directory record until the relative offset of local file header (42 from the start of central directory record, including signature length)
                    SignedPackageArchiveIOUtility.ReadAndHashUntilPosition(reader, hashFunc, reader.BaseStream.Position + 42);

                    var relativeOffsetOfLocalFileHeader = (uint)(reader.ReadUInt32() + record.ChangeInOffset);
                    HashUInt32(hashFunc, relativeOffsetOfLocalFileHeader);

                    // Continue hashing file name, extra field, and file comment fields.
                    SignedPackageArchiveIOUtility.ReadAndHashUntilPosition(reader, hashFunc, reader.BaseStream.Position + record.HeaderSize - CentralDirectoryHeader.SizeInBytesOfFixedLengthFields);
                }

                reader.BaseStream.Seek(offset: metadata.EndOfCentralDirectory, origin: SeekOrigin.Begin);

                // Hash until total entries in end of central directory record (8 bytes from the start of EOCDR)
                SignedPackageArchiveIOUtility.ReadAndHashUntilPosition(reader, hashFunc, metadata.EndOfCentralDirectory + 8);

                var eocdrTotalEntries       = (ushort)(reader.ReadUInt16() - 1);
                var eocdrTotalEntriesOnDisk = (ushort)(reader.ReadUInt16() - 1);

                HashUInt16(hashFunc, eocdrTotalEntries);
                HashUInt16(hashFunc, eocdrTotalEntriesOnDisk);

                // update the central directory size by substracting the size of the package signature file's central directory header
                var eocdrSizeOfCentralDirectory = (uint)(reader.ReadUInt32() - signatureCentralDirectoryHeader.HeaderSize);
                HashUInt32(hashFunc, eocdrSizeOfCentralDirectory);

                var eocdrOffsetOfCentralDirectory = reader.ReadUInt32() - (uint)signatureCentralDirectoryHeader.FileEntryTotalSize;
                HashUInt32(hashFunc, eocdrOffsetOfCentralDirectory);

                // Hash until the end of the reader
                SignedPackageArchiveIOUtility.ReadAndHashUntilPosition(reader, hashFunc, reader.BaseStream.Length);

                hashFunc.Update(new byte[0], offset: 0, count: 0);

                return(hashFunc.GetHash());
            }
        }
        /// <summary>
        /// Verifies that a signed package archive's signature is valid and it has not been tampered with.
        ///
        /// </summary>
        /// <param name="reader">Signed package to verify</param>
        /// <param name="hashAlgorithm">Hash algorithm to be used to hash data.</param>
        /// <param name="expectedHash">Hash value of the original data.</param>
        /// <returns>True if package archive's hash matches the expected hash</returns>
        internal static bool VerifySignedPackageIntegrity(BinaryReader reader, HashAlgorithm hashAlgorithm, byte[] expectedHash)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            if (hashAlgorithm == null)
            {
                throw new ArgumentNullException(nameof(hashAlgorithm));
            }

            if (expectedHash == null || expectedHash.Length == 0)
            {
                throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(reader));
            }

            // Make sure it is signed with a valid signature file
            if (!IsSigned(reader))
            {
                throw new SignatureException(NuGetLogCode.NU3003, Strings.SignedPackageNotSignedOnVerify);
            }

            var metadata = SignedPackageArchiveIOUtility.ReadSignedArchiveMetadata(reader);
            var signatureCentralDirectoryHeader         = metadata.GetPackageSignatureFileCentralDirectoryHeaderMetadata();
            var centralDirectoryRecordsWithoutSignature = RemoveSignatureAndOrderByOffset(metadata);

            try
            {
                // Read and hash from the start of the archive to the start of the file headers
                reader.BaseStream.Seek(offset: 0, origin: SeekOrigin.Begin);
                SignedPackageArchiveIOUtility.ReadAndHashUntilPosition(reader, hashAlgorithm, metadata.StartOfLocalFileHeaders);

                // Read and hash file headers
                foreach (var record in centralDirectoryRecordsWithoutSignature)
                {
                    reader.BaseStream.Seek(offset: record.OffsetToLocalFileHeader, origin: SeekOrigin.Begin);
                    SignedPackageArchiveIOUtility.ReadAndHashUntilPosition(reader, hashAlgorithm, record.OffsetToLocalFileHeader + record.FileEntryTotalSize);
                }

                // Order central directory records by their position
                centralDirectoryRecordsWithoutSignature.Sort((x, y) => x.Position.CompareTo(y.Position));

                // Update offset of any central directory record that has a file entry after signature
                foreach (var record in centralDirectoryRecordsWithoutSignature)
                {
                    reader.BaseStream.Seek(offset: record.Position, origin: SeekOrigin.Begin);
                    // Hash from the start of the central directory record until the relative offset of local file header (42 from the start of central directory record, including signature length)
                    SignedPackageArchiveIOUtility.ReadAndHashUntilPosition(reader, hashAlgorithm, reader.BaseStream.Position + 42);

                    var relativeOffsetOfLocalFileHeader = (uint)(reader.ReadUInt32() + record.ChangeInOffset);
                    HashUInt32(hashAlgorithm, relativeOffsetOfLocalFileHeader);

                    // Continue hashing file name, extra field, and file comment fields.
                    SignedPackageArchiveIOUtility.ReadAndHashUntilPosition(reader, hashAlgorithm, reader.BaseStream.Position + record.HeaderSize - CentralDirectoryHeader.SizeInBytesOfFixedLengthFields);
                }

                reader.BaseStream.Seek(offset: metadata.EndOfCentralDirectory, origin: SeekOrigin.Begin);

                // Hash until total entries in end of central directory record (8 bytes from the start of EOCDR)
                SignedPackageArchiveIOUtility.ReadAndHashUntilPosition(reader, hashAlgorithm, metadata.EndOfCentralDirectory + 8);

                var eocdrTotalEntries       = (ushort)(reader.ReadUInt16() - 1);
                var eocdrTotalEntriesOnDisk = (ushort)(reader.ReadUInt16() - 1);

                HashUInt16(hashAlgorithm, eocdrTotalEntries);
                HashUInt16(hashAlgorithm, eocdrTotalEntriesOnDisk);

                // update the central directory size by substracting the size of the package signature file's central directory header
                var eocdrSizeOfCentralDirectory = (uint)(reader.ReadUInt32() - signatureCentralDirectoryHeader.HeaderSize);
                HashUInt32(hashAlgorithm, eocdrSizeOfCentralDirectory);

                var eocdrOffsetOfCentralDirectory = reader.ReadUInt32() - (uint)signatureCentralDirectoryHeader.FileEntryTotalSize;
                HashUInt32(hashAlgorithm, eocdrOffsetOfCentralDirectory);

                // Hash until the end of the reader
                SignedPackageArchiveIOUtility.ReadAndHashUntilPosition(reader, hashAlgorithm, reader.BaseStream.Length);

                hashAlgorithm.TransformFinalBlock(new byte[0], inputOffset: 0, inputCount: 0);

                return(CompareHash(expectedHash, hashAlgorithm.Hash));
            }
            // If exception is throw in means the archive was not a valid package. It has been tampered, return false.
            catch { }

            return(false);
        }
 /// <summary>
 /// Signs a Zip with the contents in the SignatureStream using the writer.
 /// The reader is used to read the exisiting contents for the Zip.
 /// </summary>
 /// <param name="signatureStream">MemoryStream of the signature to be inserted into the zip.</param>
 /// <param name="reader">BinaryReader to be used to read the existing zip data.</param>
 /// <param name="writer">BinaryWriter to be used to write the signature into the zip.</param>
 internal static void SignZip(MemoryStream signatureStream, BinaryReader reader, BinaryWriter writer)
 {
     SignedPackageArchiveIOUtility.WriteSignatureIntoZip(signatureStream, reader, writer);
 }