winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit f320a207d2e32415c501ffd5a4b2a7462748c7a5
parent 6f06d916641d380afed76780c215ced9d55988e9
Author: yao-msft <50888816+yao-msft@users.noreply.github.com>
Date:   Thu, 12 Mar 2020 15:32:34 -0700

Implement hash command (#53)


Diffstat:
Msrc/AppInstallerCLICore/AppInstallerCLICore.vcxproj | 2++
Msrc/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters | 6++++++
Asrc/AppInstallerCLICore/Commands/HashCommand.cpp | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerCLICore/Commands/HashCommand.h | 20++++++++++++++++++++
Msrc/AppInstallerCLICore/Commands/RootCommand.cpp | 2++
Msrc/AppInstallerCLICore/ExecutionArgs.h | 4++++
Msrc/AppInstallerCLICore/Workflows/MsixInstallerHandler.cpp | 3+--
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj | 4++++
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters | 6++++++
Asrc/AppInstallerCLITests/HashCommand.cpp | 24++++++++++++++++++++++++
Asrc/AppInstallerCLITests/TestData/TestSignedApp.msix | 0
Msrc/AppInstallerCommonCore/Public/AppInstallerSHA256.h | 5++++-
Msrc/AppInstallerCommonCore/SHA256.cpp | 27++++++++++++++++++++++++---
13 files changed, 156 insertions(+), 6 deletions(-)

diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -169,6 +169,7 @@ </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="Command.h" /> + <ClInclude Include="Commands\HashCommand.h" /> <ClInclude Include="Commands\SearchCommand.h" /> <ClInclude Include="Commands\ShowCommand.h" /> <ClInclude Include="Commands\InstallCommand.h" /> @@ -194,6 +195,7 @@ </ItemGroup> <ItemGroup> <ClCompile Include="Command.cpp" /> + <ClCompile Include="Commands\HashCommand.cpp" /> <ClCompile Include="Commands\SearchCommand.cpp" /> <ClCompile Include="Commands\ShowCommand.cpp" /> <ClCompile Include="Commands\InstallCommand.cpp" /> diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -93,6 +93,9 @@ <ClInclude Include="ExecutionArgs.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="Commands\HashCommand.h"> + <Filter>Commands</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="pch.cpp"> @@ -146,6 +149,9 @@ <ClCompile Include="ExecutionReporter.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="Commands\HashCommand.cpp"> + <Filter>Commands</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="PropertySheet.props" /> diff --git a/src/AppInstallerCLICore/Commands/HashCommand.cpp b/src/AppInstallerCLICore/Commands/HashCommand.cpp @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "HashCommand.h" +#include "Localization.h" + +namespace AppInstaller::CLI +{ + using namespace std::string_view_literals; + + constexpr std::string_view s_HashCommand_ArgName_File = "file"sv; + constexpr std::string_view s_HashCommand_ArgName_Msix = "msix"sv; + + std::vector<Argument> HashCommand::GetArguments() const + { + return { + Argument{ s_HashCommand_ArgName_File, ExecutionArgs::Type::HashFile, LOCME("The input file to be hashed."), ArgumentType::Positional, true }, + Argument{ s_HashCommand_ArgName_Msix, ExecutionArgs::Type::Msix, LOCME("If specified, the input file will be treated as msix. Signature hash will be provided if exists."), ArgumentType::Flag }, + }; + } + + std::string HashCommand::ShortDescription() const + { + return LOCME("Helper to hash installer files"); + } + + std::vector<std::string> HashCommand::GetLongDescription() const + { + return { + LOCME("Helper to hash installer files"), + }; + } + + void HashCommand::ExecuteInternal(ExecutionContext& context) const + { + auto inputFile = context.Args.GetArg(ExecutionArgs::Type::HashFile); + std::ifstream inStream{ *inputFile, std::ifstream::binary }; + + context.Reporter.ShowMsg("File Hash: " + Utility::SHA256::ConvertToString(Utility::SHA256::ComputeHash(inStream))); + + if (context.Args.Contains(ExecutionArgs::Type::Msix)) + { + try + { + Msix::MsixInfo msixInfo{ *inputFile }; + auto signature = msixInfo.GetSignature(); + auto signatureHash = Utility::SHA256::ComputeHash(signature.data(), static_cast<uint32_t>(signature.size())); + + context.Reporter.ShowMsg("Signature Hash: " + Utility::SHA256::ConvertToString(signatureHash)); + } + catch (const wil::ResultException&) + { + context.Reporter.ShowMsg( + "Failed to calculate signature hash. Please verify the input file is a valid signed msix.", + ExecutionReporter::Level::Warning); + } + } + } +} diff --git a/src/AppInstallerCLICore/Commands/HashCommand.h b/src/AppInstallerCLICore/Commands/HashCommand.h @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Command.h" + +namespace AppInstaller::CLI +{ + struct HashCommand final : public Command + { + HashCommand() : Command("hash") {} + + virtual std::vector<Argument> GetArguments() const override; + + virtual std::string ShortDescription() const override; + virtual std::vector<std::string> GetLongDescription() const override; + + protected: + void ExecuteInternal(ExecutionContext& context) const override; + }; +} diff --git a/src/AppInstallerCLICore/Commands/RootCommand.cpp b/src/AppInstallerCLICore/Commands/RootCommand.cpp @@ -8,6 +8,7 @@ #include "ShowCommand.h" #include "SourceCommand.h" #include "SearchCommand.h" +#include "HashCommand.h" namespace AppInstaller::CLI { @@ -18,6 +19,7 @@ namespace AppInstaller::CLI std::make_unique<ShowCommand>(), std::make_unique<SourceCommand>(), std::make_unique<SearchCommand>(), + std::make_unique<HashCommand>(), }); } diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h @@ -42,6 +42,10 @@ namespace AppInstaller::CLI SourceType, SourceArg, + //Hash Command + HashFile, + Msix, // Flag to indicate the input file is msix + // Other ListVersions, // Used in Show command to list all available versions of an app Help, // Show command usage diff --git a/src/AppInstallerCLICore/Workflows/MsixInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/MsixInstallerHandler.cpp @@ -27,8 +27,7 @@ namespace AppInstaller::Workflow Msix::MsixInfo msixInfo(m_manifestInstallerRef.Url); auto signature = msixInfo.GetSignature(); - SHA256::HashBuffer signatureHash; - SHA256::ComputeHash(signature.data(), static_cast<uint32_t>(signature.size()), signatureHash); + auto signatureHash = SHA256::ComputeHash(signature.data(), static_cast<uint32_t>(signature.size())); if (!std::equal( m_manifestInstallerRef.SignatureSha256.begin(), diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -152,6 +152,7 @@ </ItemGroup> <ItemGroup> <ClCompile Include="Downloader.cpp" /> + <ClCompile Include="HashCommand.cpp" /> <ClCompile Include="MsixInfo.cpp" /> <ClCompile Include="PreIndexedPackageSource.cpp" /> <ClCompile Include="SQLiteIndexSource.cpp" /> @@ -213,6 +214,9 @@ <CopyFileToFolders Include="TestData\index.2.0.0.0.msix"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\TestSignedApp.msix"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> </ItemGroup> <ItemGroup> <ProjectReference Include="..\AppInstallerCLICore\AppInstallerCLICore.vcxproj"> diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -74,6 +74,9 @@ <ClCompile Include="SQLiteIndexSource.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="HashCommand.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="PropertySheet.props" /> @@ -116,5 +119,8 @@ <CopyFileToFolders Include="TestData\index.2.0.0.0.msix"> <Filter>TestData</Filter> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\TestSignedApp.msix"> + <Filter>TestData</Filter> + </CopyFileToFolders> </ItemGroup> </Project> \ No newline at end of file diff --git a/src/AppInstallerCLITests/HashCommand.cpp b/src/AppInstallerCLITests/HashCommand.cpp @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestCommon.h" +#include "Commands/HashCommand.h" + +using namespace std::string_literals; +using namespace TestCommon; +using namespace AppInstaller::CLI; + +TEST_CASE("HashCommandWithTestMsix", "[Sha256Hash]") +{ + std::ostringstream hashOutput; + ExecutionContext context{ hashOutput, std::cin }; + context.Args.AddArg(ExecutionArgs::Type::HashFile, TestDataFile("TestSignedApp.msix").GetPath().u8string()); + context.Args.AddArg(ExecutionArgs::Type::Msix); + HashCommand hashCommand; + + hashCommand.Execute(context); + + REQUIRE(hashOutput.str().find("File Hash: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea") != std::string::npos); + REQUIRE(hashOutput.str().find("Signature Hash: 138781c3e6f635240353f3d14d1d57bdcb89413e49be63b375e6a5d7b93b0d07") != std::string::npos); +}+ \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestData/TestSignedApp.msix b/src/AppInstallerCLITests/TestData/TestSignedApp.msix Binary files differ. diff --git a/src/AppInstallerCommonCore/Public/AppInstallerSHA256.h b/src/AppInstallerCommonCore/Public/AppInstallerSHA256.h @@ -42,7 +42,10 @@ namespace AppInstaller::Utility { } // Computes the hash of the given buffer immediately. - static bool ComputeHash(uint8_t* buffer, std::uint32_t cbBuffer, HashBuffer& hash); + static std::vector<uint8_t> ComputeHash(uint8_t* buffer, std::uint32_t cbBuffer); + + // Computes the hash from a given stream. + static std::vector<uint8_t> ComputeHash(std::istream& in); static std::string ConvertToString(const HashBuffer& hashBuffer); diff --git a/src/AppInstallerCommonCore/SHA256.cpp b/src/AppInstallerCommonCore/SHA256.cpp @@ -128,13 +128,34 @@ namespace AppInstaller::Utility { return resultBuffer; } - bool SHA256::ComputeHash(std::uint8_t* buffer, std::uint32_t cbBuffer, HashBuffer& hash) + std::vector<uint8_t> SHA256::ComputeHash(std::uint8_t* buffer, std::uint32_t cbBuffer) { SHA256 hasher; hasher.Add(buffer, cbBuffer); - hasher.Get(hash); - return true; + std::vector<uint8_t> result; + hasher.Get(result); + + return result; + } + + std::vector<uint8_t> SHA256::ComputeHash(std::istream& in) + { + const int bufferSize = 1024 * 1024; // 1MB + auto buffer = std::make_unique<uint8_t[]>(bufferSize); + + SHA256 hasher; + + while (!in.eof()) + { + in.read((char*)(buffer.get()), bufferSize); + hasher.Add(buffer.get(), in.gcount()); + } + + std::vector<uint8_t> result; + hasher.Get(result); + + return result; } void SHA256::SHA256ContextDeleter::operator()(SHA256Context* context)