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:
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)