File: Verification\Jar\JarManifestFileBase.cs
Web Access
Project: src\src\SignCheck\Microsoft.SignCheck\Microsoft.DotNet.SignCheckLibrary.csproj (Microsoft.DotNet.SignCheckLibrary)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.IO.Compression;
 
namespace Microsoft.SignCheck.Verification.Jar
{
    /// <summary>
    /// Base class for manifest files.
    /// </summary>
    public class JarManifestFileBase
    {
        // Backing fields
        private JarAttributes _mainAttributes;
        private JarIndividualSection _individualSection;
 
        public string ArchivePath
        {
            get;
            private set;
        }
 
        public JarAttributes MainAttributes
        {
            get
            {
                if (_mainAttributes == null)
                {
                    _mainAttributes = JarAttributes.From(MainSectionText);
                }
 
                return _mainAttributes;
            }
        }
 
        public JarIndividualSection IndividualSection
        {
            get
            {
                if (_individualSection == null)
                {
                    _individualSection = new JarIndividualSection(IndividualSectionText);
                }
 
                return _individualSection;
            }
        }
 
        public string ManifestPath
        {
            get;
            private set;
        }
 
        /// <summary>
        /// The raw text comprising the individual-section of the manifest.
        /// </summary>
        protected string IndividualSectionText
        {
            get;
            private set;
        }
 
        /// <summary>
        /// The raw text comprising the main-section of the manifest.
        /// </summary>
        protected string MainSectionText
        {
            get;
            private set;
        }
 
        /// <summary>
        /// The raw text comprising the complete manifest.
        /// </summary>
        protected string ManifestText
        {
            get;
            private set;
        }
 
        public JarManifestFileBase(string archivePath, string manifestPath)
        {
            ArchivePath = archivePath;
            ManifestPath = manifestPath;
 
            using (ZipArchive archive = ZipFile.Open(ArchivePath, ZipArchiveMode.Read))
            {
                ZipArchiveEntry manifestArchiveEntry = archive.Entries.First(a => String.Equals(a.FullName, ManifestPath, StringComparison.OrdinalIgnoreCase));
 
                if (manifestArchiveEntry != null)
                {
                    // Parse the manifest file by splitting the Main and Individual sections. The actual attributes
                    // will be parsed on demand as they're accessed.
                    using (Stream manifestStream = manifestArchiveEntry.Open())
                    using (var manifestStreamReader = new StreamReader(manifestStream, Encoding.UTF8))
                    {
                        // It's important to use ReadToEnd instead of something like ReadLines
                        // as the CR/LF bytes need to be preserved when calculating digests
                        // for the different sections.
                        ManifestText = manifestStreamReader.ReadToEnd();
 
                        // The start of the individual section (first "Name: xxxx" entry)
                        // indicates the end of the Main section. Avoid x-Name: attributes.
                        // The first individual Name: attribute will be preceded by either CR+LF | CR | LF
                        int crNameIndex = ManifestText.IndexOf("\rName:");
                        int lfNameIndex = ManifestText.IndexOf("\nName:");
 
                        int mainSectionLength = crNameIndex > 0 ? crNameIndex + 1 : lfNameIndex + 1;
                        //int mainSectionLength = ManifestText.IndexOf("\rName:");
                        MainSectionText = ManifestText.Substring(0, mainSectionLength);
 
                        // Anything remaining should be the individual section
                        IndividualSectionText = ManifestText.Substring(mainSectionLength);
                    }
                }
            }
        }
 
        /// <summary>
        /// Computes the hash digest (encoded as Base64) of the full manifest text using the specified hash algorithm.
        /// </summary>
        /// <param name="algorithmName">The name of the hash algorithm to use.</param>
        /// <returns>A base64 encoded string of the manifest hash.</returns>
        public string GetManifestDigest(string algorithmName)
        {
            using (HashAlgorithm hashAlgorithm = HashAlgorithm.Create(algorithmName))
            {
                byte[] hashValue = hashAlgorithm.ComputeHash(new UTF8Encoding().GetBytes(ManifestText.ToCharArray()));
                return Convert.ToBase64String(hashValue);
            }
        }
    }
}