/// <summary>
        ///     Digitally signs the specified manifest, adds the signature to the manifest, and returns it.
        /// </summary>
        /// <param name="manifest">The manifest to sign.</param>
        /// <param name="privateKey">The PGP private key with which to sign the file.</param>
        /// <param name="passphrase">The passphrase for the PGP private key.</param>
        /// <param name="keybaseUsername">
        ///     The Keybase.io username of the account hosting the PGP public key used for digest verification.
        /// </param>
        /// <returns>The signed manifest.</returns>
        private PackageManifest SignManifest(PackageManifest manifest, string privateKey, string passphrase, string keybaseUsername)
        {
            Info("Digitally signing manifest...");

            // insert a signature into the manifest. the signer must be included in the hash to prevent tampering.
            PackageManifestSignature signature = new PackageManifestSignature();

            signature.Issuer   = PackagingConstants.KeyIssuer;
            signature.Subject  = keybaseUsername;
            manifest.Signature = signature;

            Verbose("Creating SHA512 hash of serialized manifest...");
            string manifestHash = Utility.ComputeSHA512Hash(manifest.ToJson());

            Verbose($"Hash computed successfully: {manifestHash}.");

            byte[] manifestBytes = Encoding.ASCII.GetBytes(manifest.ToJson());
            Verbose("Creating digest...");
            byte[] digestBytes = PGPSignature.Sign(manifestBytes, privateKey, passphrase);
            Verbose("Digest created successfully.");

            Verbose("Adding signature to manifest...");
            manifest.Signature.Digest = Encoding.ASCII.GetString(digestBytes);
            Success("Manifest signed successfully.");

            return(manifest);
        }
        /// <summary>
        ///     Serializes the specified manifest to JSON and writes it to a 'manifest.json' file in the specified directory.
        /// </summary>
        /// <param name="manifest">The manifest to serialize and write.</param>
        /// <param name="directory">The directory into which the generated file will be written.</param>
        private void WriteManifest(PackageManifest manifest, string directory)
        {
            string destinationFile = Path.Combine(directory, PackagingConstants.ManifestFilename);
            string contents        = manifest.ToJson();

            File.WriteAllText(destinationFile, contents);
        }
Exemple #3
0
        /// <summary>
        ///     Updates the specified Package, replacing the existing Manifest with the specified Manifest.
        /// </summary>
        /// <param name="packageFile">The filename of the Package file to update.</param>
        /// <param name="manifest">The Manifest with which the Package file will be updated.</param>
        /// <param name="tempDirectory">The path to the temporary directory to use for file operations.</param>
        private void UpdatePackageManifest(string packageFile, PackageManifest manifest, string tempDirectory)
        {
            Verbose($"Updating Manifest in Package '{Path.GetFileName(packageFile)}'...");

            string tempPackageDirectory = Path.Combine(tempDirectory, "package");
            string tempManifest         = Path.Combine(tempPackageDirectory, PackagingConstants.ManifestFilename);
            string tempPackage          = Path.Combine(tempDirectory, Path.GetFileName(packageFile));

            Verbose($"Extracting Package to '{tempPackageDirectory}'...");
            ZipFile.ExtractToDirectory(packageFile, tempPackageDirectory);
            Verbose("Package extracted successfully.");

            Verbose($"Replacing Manifest file...");
            File.Delete(tempManifest);
            File.WriteAllText(tempManifest, manifest.ToJson());
            Verbose("Manifest file replaced successfully.");

            Verbose($"Compressing Package...");
            ZipFile.CreateFromDirectory(tempPackageDirectory, tempPackage);
            Verbose("Package compressed successfully.");

            Verbose($"Overwriting original Package...");
            File.Copy(tempPackage, packageFile, true);
            Verbose("Package overwritten successfully.");
        }
Exemple #4
0
        /// <summary>
        ///     Verifies the Digest contained in the specified Manifest using the specified PGP Public Key.
        /// </summary>
        /// <param name="manifest">The Manifest for which the Digest is to be verified.</param>
        /// <param name="publicKey">The PGP Public Key with which to verify the Digest.</param>
        /// <exception cref="InvalidDataException">
        ///     Thrown when an error is encountered verifying the Digest, or when the Manifest contents do not match the verified Digest.
        /// </exception>
        private void VerifyDigest(PackageManifest manifest, string publicKey)
        {
            string verifiedDigest = string.Empty;

            if (!string.IsNullOrEmpty(manifest.Signature.Digest))
            {
                Verbose("Verifying the Manifest Digest...");

                byte[] digestBytes = Encoding.ASCII.GetBytes(manifest.Signature.Digest);
                byte[] verifiedDigestBytes;

                try
                {
                    verifiedDigestBytes = PGPSignature.Verify(digestBytes, publicKey);
                }
                catch (Exception ex)
                {
                    throw new InvalidDataException($"an Exception was thrown while verifying the Digest: {ex.GetType().Name}: {ex.Message}", ex);
                }

                verifiedDigest = Encoding.ASCII.GetString(verifiedDigestBytes);

                // deserialize the verified manifest to work around text formatting differences on various platforms
                PackageManifest verifiedManifest;

                try
                {
                    verifiedManifest = JsonConvert.DeserializeObject <PackageManifest>(verifiedDigest);
                }
                catch (Exception ex)
                {
                    throw new InvalidDataException($"an Exception was thrown while deserializing the Digest: {ex.GetType().Name}: {ex.Message}", ex);
                }

                // remove the digest and trust from the manifest, then serialize it and compare it to the verified digest.
                manifest.Signature.Digest = default(string);
                manifest.Signature.Trust  = default(string);

                // if the scrubbed manifest and verified digest don't match, something was tampered with.
                if (manifest.ToJson() != verifiedManifest.ToJson())
                {
                    throw new InvalidDataException("the Manifest Digest is not valid; the verified Digest does not match the Manifest.");
                }

                Verbose("Digest verified successfully.");
            }
            else
            {
                throw new InvalidDataException("the Manifest Digest is null or empty.");
            }
        }
        /// <summary>
        ///     Generates and populates <see cref="IPackageManifest"/> objects from the specified directory, including resource
        ///     files and hashing file entries if those options are specified.
        /// </summary>
        /// <param name="inputDirectory">The directory from which to generate a list of files.</param>
        /// <param name="hashFiles">A value indicating whether files added to the manifest are to include a SHA512 hash.</param>
        /// <param name="manifestFile">The filename of the file to which the manifest is to be saved.</param>
        /// <returns>The generated manifest.</returns>
        public IPackageManifest GenerateManifest(string inputDirectory, bool hashFiles = false, string manifestFile = "")
        {
            ArgumentValidator.ValidateInputDirectoryArgument(inputDirectory);

            string[] files = Directory.GetFiles(inputDirectory, "*", SearchOption.AllDirectories);

            PackageManifestBuilder builder = new PackageManifestBuilder();

            Info($"Generating manifest for directory '{inputDirectory}'...");
            builder.BuildDefault();
            Verbose($"Adding files from '{inputDirectory}'...");

            foreach (string file in files)
            {
                AddFile(builder, file, inputDirectory, hashFiles);
            }

            PackageManifest manifest = builder.Manifest;

            Success("Manifest generated successfully.");

            if (!string.IsNullOrEmpty(manifestFile))
            {
                try
                {
                    Info($"Saving output to file '{manifestFile}'...");
                    File.WriteAllText(manifestFile, manifest.ToJson());
                    Success("File saved successfully.");
                }
                catch (Exception ex)
                {
                    throw new Exception($"Unable to write to output file '{manifestFile}': {ex.Message}", ex);
                }
            }

            return(manifest);
        }
        /// <summary>
        ///     Extracts the <see cref="PackageManifest"/> object from the specified Package file.
        /// </summary>
        /// <param name="packageFile">The Package from which the manifest is to be extracted.</param>
        /// <param name="manifestFile">The filename of the file to which the manifest is to be saved.</param>
        /// <returns>The extracted manifest object.</returns>
        public PackageManifest ExtractManifest(string packageFile, string manifestFile = "")
        {
            ArgumentValidator.ValidatePackageFileArgumentForReading(packageFile);

            Info($"Extracting manifest '{PackagingConstants.ManifestFilename}' from package '{packageFile}'...");

            PackageManifest manifest = new PackageManifest();

            ZipArchive   archive = default(ZipArchive);
            StreamReader reader  = default(StreamReader);

            try
            {
                Verbose($"Locating manifest inside of package...");

                archive = ZipFile.OpenRead(packageFile);
                ZipArchiveEntry zippedManifestFile = archive.Entries.Where(e => e.Name == PackagingConstants.ManifestFilename).FirstOrDefault();
                string          manifestString;

                if (zippedManifestFile != default(ZipArchiveEntry))
                {
                    Verbose("Manifest located successfully.");

                    Verbose("Reading manifest from package...");
                    reader         = new StreamReader(zippedManifestFile.Open());
                    manifestString = reader.ReadToEnd();
                    Verbose("Manifest read successfully.");
                }
                else
                {
                    throw new FileNotFoundException($"The package '{Path.GetFileName(packageFile)}' does not contain a manifest.");
                }

                Verbose("Deserializing manifest...");
                manifest = JsonConvert.DeserializeObject <PackageManifest>(manifestString);
                Verbose("Manifest deserialized successfully.");
            }
            catch (JsonException ex)
            {
                throw new JsonException($"The manifest within package '{Path.GetFileName(packageFile)}' is malformed: {ex.Message}");
            }
            catch (Exception ex)
            {
                throw new IOException($"Error extracting manifest from package '{Path.GetFileName(packageFile)}': {ex.Message}");
            }
            finally
            {
                archive?.Dispose();
                reader?.Close();
            }

            Success("Manifest extracted successfully.");

            if (!string.IsNullOrEmpty(manifestFile))
            {
                try
                {
                    Info($"Saving extracted manifest to file '{manifestFile}'...");
                    File.WriteAllText(manifestFile, manifest.ToJson());
                    Info("File saved successfully.");
                }
                catch (Exception ex)
                {
                    throw new Exception($"Unable to write to manifest file '{manifestFile}': {ex.Message}", ex);
                }
            }

            return(manifest);
        }
        /// <summary>
        ///     Processes the desired command with the arguments specified in the command line arguments from Main().
        /// </summary>
        /// <param name="args">
        ///     The optional command line arguments, used to override the arguments with which the application was started.
        /// </param>
        public static void Process(string args = "")
        {
            if (args != string.Empty)
            {
                Arguments.Populate(args);
            }

            string command = string.Empty;

            try
            {
                if (Operands.Count > 1)
                {
                    command = Operands[1].ToLower();
                }

                if (command == "manifest")
                {
                    ManifestGenerator generator = new ManifestGenerator();
                    generator.Updated += Update;

                    PackageManifest manifest = generator.GenerateManifest(Directory, HashFiles, ManifestFile);

                    if (string.IsNullOrEmpty(ManifestFile) && manifest != default(PackageManifest))
                    {
                        Console.WriteLine(manifest.ToJson());
                    }
                }
                else if (command == "extract-manifest")
                {
                    ManifestExtractor extractor = new ManifestExtractor();
                    extractor.Updated += Update;

                    PackageManifest manifest = extractor.ExtractManifest(PackageFile, ManifestFile);

                    if (string.IsNullOrEmpty(ManifestFile) && manifest != default(PackageManifest))
                    {
                        Console.WriteLine(manifest.ToJson());
                    }
                }
                else if (command == "package")
                {
                    PackageCreator creator = new PackageCreator();
                    creator.Updated += Update;

                    string privateKey = string.Empty;

                    if (!string.IsNullOrEmpty(PrivateKeyFile))
                    {
                        privateKey = File.ReadAllText(PrivateKeyFile);
                    }

                    creator.CreatePackage(Directory, ManifestFile, PackageFile, SignPackage, privateKey, Passphrase, KeybaseUsername);
                }
                else if (command == "extract-package")
                {
                    PackageExtractor extractor = new PackageExtractor();
                    extractor.Updated += Update;

                    string publicKey = string.Empty;

                    if (!string.IsNullOrEmpty(PublicKeyFile))
                    {
                        publicKey = File.ReadAllText(PublicKeyFile);
                    }

                    extractor.ExtractPackage(PackageFile, Directory, publicKey, Overwrite, SkipVerification);
                }
                else if (command == "trust")
                {
                    PackageTruster truster = new PackageTruster();
                    truster.Updated += Update;
                    truster.TrustPackage(PackageFile, File.ReadAllText(PrivateKeyFile), Passphrase);
                }
                else if (command == "verify")
                {
                    PackageVerifier verifier = new PackageVerifier();
                    verifier.Updated += Update;

                    string publicKey = string.Empty;

                    if (!string.IsNullOrEmpty(PublicKeyFile))
                    {
                        publicKey = File.ReadAllText(PublicKeyFile);
                    }

                    verifier.VerifyPackage(PackageFile, publicKey);
                }
                else
                {
                    HelpPrinter.PrintHelp(Operands.Count > 2 ? Operands[2] : default(string));
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }
        }