winget-cli

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

MsixInfo.cpp (32194B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "Public/AppInstallerMsixInfo.h"
      5 #include "HttpStream/HttpRandomAccessStream.h"
      6 #include "Public/AppInstallerDownloader.h"
      7 #include "Public/AppInstallerLogging.h"
      8 #include "Public/AppInstallerStrings.h"
      9 #include "Public/AppInstallerDownloader.h"
     10 #include "Public/AppInstallerRuntime.h"
     11 
     12 using namespace winrt::Windows::Storage::Streams;
     13 using namespace Microsoft::WRL;
     14 using namespace AppInstaller::Utility::HttpStream;
     15 using namespace winrt::Windows::Management::Deployment;
     16 
     17 namespace AppInstaller::Msix
     18 {
     19     namespace
     20     {
     21         // MSIX-specific header placed in the P7X file, before the actual signature
     22         const byte P7xFileId[] = { 0x50, 0x4b, 0x43, 0x58 };
     23         const DWORD P7xFileIdSize = sizeof(P7xFileId);
     24 
     25         // Gets the version from the manifest reader.
     26         UINT64 GetVersionFromManifestReader(IAppxManifestReader* reader)
     27         {
     28             ComPtr<IAppxManifestPackageId> packageId;
     29             THROW_IF_FAILED(reader->GetPackageId(&packageId));
     30 
     31             UINT64 result = 0;
     32             THROW_IF_FAILED(packageId->GetVersion(&result));
     33 
     34             return result;
     35         }
     36 
     37         // Gets the UINT64 version from the version struct.
     38         UINT64 GetVersionFromVersion(const winrt::Windows::ApplicationModel::PackageVersion& version)
     39         {
     40             UINT64 result = version.Major;
     41             result = (result << 16) | version.Minor;
     42             result = (result << 16) | version.Build;
     43             result = (result << 16) | version.Revision;
     44 
     45             return result;
     46         }
     47 
     48         // Writes the stream (from current location) to the given file.
     49         void WriteStreamToFile(IStream* stream, UINT64 expectedSize, const std::filesystem::path& target, IProgressCallback& progress)
     50         {
     51             std::filesystem::path tempFile = target;
     52             tempFile += ".dnld";
     53 
     54             {
     55                 std::ofstream file(tempFile, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
     56 
     57                 constexpr ULONG bufferSize = 1 << 20;
     58                 std::unique_ptr<char[]> buffer = std::make_unique<char[]>(bufferSize);
     59 
     60                 UINT64 totalBytesRead = 0;
     61 
     62                 while (!progress.IsCancelledBy(CancelReason::Any))
     63                 {
     64                     ULONG bytesRead = 0;
     65                     HRESULT hr = stream->Read(buffer.get(), bufferSize, &bytesRead);
     66 
     67                     if (bytesRead)
     68                     {
     69                         // If we got bytes, just accept them and keep going.
     70                         LOG_IF_FAILED(hr);
     71 
     72                         THROW_HR_IF_MSG(E_UNEXPECTED, expectedSize && totalBytesRead + bytesRead > expectedSize, "Read more bytes than expected size");
     73 
     74                         file.write(buffer.get(), bytesRead);
     75                         totalBytesRead += bytesRead;
     76                         progress.OnProgress(totalBytesRead, expectedSize, ProgressType::Bytes);
     77                     }
     78                     else
     79                     {
     80                         // If given a size, and we have read it all, quit
     81                         if (expectedSize && totalBytesRead == expectedSize)
     82                         {
     83                             break;
     84                         }
     85 
     86                         // If the stream returned an error, throw it
     87                         THROW_IF_FAILED(hr);
     88 
     89                         // If we were given a size and didn't reach it, throw our own error;
     90                         // otherwise assume that this is just normal EOF.
     91                         if (expectedSize)
     92                         {
     93                             THROW_WIN32(ERROR_HANDLE_EOF);
     94                         }
     95                         else
     96                         {
     97                             break;
     98                         }
     99                     }
    100                 }
    101             }
    102 
    103             std::filesystem::path backupFile = target;
    104             backupFile += ".bkup";
    105             if (std::filesystem::exists(target))
    106             {
    107                 if (std::filesystem::exists(backupFile))
    108                 {
    109                     std::filesystem::remove(backupFile);
    110                 }
    111                 std::filesystem::rename(target, backupFile);
    112             }
    113 
    114             std::filesystem::rename(tempFile, target);
    115         }
    116 
    117         // Writes the appx file to the given file.
    118         void WriteAppxFileToFile(IAppxFile* appxFile, const std::filesystem::path& target, IProgressCallback& progress)
    119         {
    120             UINT64 size = 0;
    121             THROW_IF_FAILED(appxFile->GetSize(&size));
    122 
    123             ComPtr<IStream> stream;
    124             THROW_IF_FAILED(appxFile->GetStream(&stream));
    125 
    126             WriteStreamToFile(stream.Get(), size, target, progress);
    127         }
    128 
    129         // Writes the stream (from current location) to the given file handle.
    130         void WriteStreamToFileHandle(IStream* stream, UINT64 expectedSize, HANDLE target, IProgressCallback& progress)
    131         {
    132             constexpr ULONG bufferSize = 1 << 20;
    133             std::unique_ptr<char[]> buffer = std::make_unique<char[]>(bufferSize);
    134 
    135             UINT64 totalBytesRead = 0;
    136 
    137             while (!progress.IsCancelledBy(CancelReason::Any))
    138             {
    139                 ULONG bytesRead = 0;
    140                 HRESULT hr = stream->Read(buffer.get(), bufferSize, &bytesRead);
    141 
    142                 if (bytesRead)
    143                 {
    144                     // If we got bytes, just accept them and keep going.
    145                     LOG_IF_FAILED(hr);
    146 
    147                     THROW_HR_IF_MSG(E_UNEXPECTED, expectedSize && totalBytesRead + bytesRead > expectedSize, "Read more bytes than expected size");
    148 
    149                     DWORD bytesWritten = 0;
    150                     THROW_LAST_ERROR_IF(!WriteFile(target, buffer.get(), bytesRead, &bytesWritten, nullptr));
    151                     THROW_HR_IF(E_UNEXPECTED, bytesRead != bytesWritten);
    152                     totalBytesRead += bytesRead;
    153                     progress.OnProgress(totalBytesRead, expectedSize, ProgressType::Bytes);
    154                 }
    155                 else
    156                 {
    157                     // If given a size, and we have read it all, quit
    158                     if (expectedSize && totalBytesRead == expectedSize)
    159                     {
    160                         break;
    161                     }
    162 
    163                     // If the stream returned an error, throw it
    164                     THROW_IF_FAILED(hr);
    165 
    166                     // If we were given a size and didn't reach it, throw our own error;
    167                     // otherwise assume that this is just normal EOF.
    168                     if (expectedSize)
    169                     {
    170                         THROW_WIN32(ERROR_HANDLE_EOF);
    171                     }
    172                     else
    173                     {
    174                         break;
    175                     }
    176                 }
    177             }
    178         }
    179 
    180         // Writes the appx file to the given file handle.
    181         void WriteAppxFileToFileHandle(IAppxFile* appxFile, HANDLE target, IProgressCallback& progress)
    182         {
    183             UINT64 size = 0;
    184             THROW_IF_FAILED(appxFile->GetSize(&size));
    185 
    186             ComPtr<IStream> stream;
    187             THROW_IF_FAILED(appxFile->GetStream(&stream));
    188 
    189             WriteStreamToFileHandle(stream.Get(), size, target, progress);
    190         }
    191 
    192         bool ValidateMsixTrustInfo(const std::filesystem::path& msixPath, bool verifyMicrosoftOrigin)
    193         {
    194             bool result = false;
    195             AICLI_LOG(Core, Info, << "Started trust validation of msix at: " << msixPath);
    196 
    197             try
    198             {
    199                 bool verifyChainResult = false;
    200 
    201                 // First verify certificate chain if requested.
    202                 if (verifyMicrosoftOrigin)
    203                 {
    204                     auto [certContext, certStore] = GetCertContextFromMsix(msixPath);
    205 
    206                     // Get certificate chain context for validation
    207                     CERT_CHAIN_PARA certChainParameters = { 0 };
    208                     certChainParameters.cbSize = sizeof(CERT_CHAIN_PARA);
    209                     certChainParameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
    210                     DWORD certChainFlags = CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL;
    211 
    212                     wil::unique_cert_chain_context certChainContext;
    213                     THROW_LAST_ERROR_IF(!CertGetCertificateChain(
    214                         HCCE_LOCAL_MACHINE,
    215                         certContext.get(),
    216                         NULL,   // Use the current system time for CRL validation
    217                         certStore.get(),
    218                         &certChainParameters,
    219                         certChainFlags,
    220                         NULL,   // Reserved parameter; must be NULL
    221                         &certChainContext));
    222 
    223                     // Validate that the certificate chain is rooted in one of the well-known Microsoft root certs
    224                     CERT_CHAIN_POLICY_PARA policyParameters = { 0 };
    225                     policyParameters.cbSize = sizeof(CERT_CHAIN_POLICY_PARA);
    226                     policyParameters.dwFlags = MICROSOFT_ROOT_CERT_CHAIN_POLICY_CHECK_APPLICATION_ROOT_FLAG;
    227                     CERT_CHAIN_POLICY_STATUS policyStatus = { 0 };
    228                     policyStatus.cbSize = sizeof(CERT_CHAIN_POLICY_STATUS);
    229                     LPCSTR policyOid = CERT_CHAIN_POLICY_MICROSOFT_ROOT;
    230                     BOOL certChainVerifySucceeded = CertVerifyCertificateChainPolicy(
    231                         policyOid,
    232                         certChainContext.get(),
    233                         &policyParameters,
    234                         &policyStatus);
    235 
    236                     AICLI_LOG(Core, Info, << "Result for certificate chain validation of Microsoft origin: " << policyStatus.dwError);
    237 
    238                     verifyChainResult = certChainVerifySucceeded && policyStatus.dwError == ERROR_SUCCESS;
    239                 }
    240                 else
    241                 {
    242                     verifyChainResult = true;
    243                 }
    244 
    245                 // If certificate chain origin validation is success or not requested, then validate the trust info of the file.
    246                 if (verifyChainResult)
    247                 {
    248                     // Set up the structures needed for the WinVerifyTrust call
    249                     WINTRUST_FILE_INFO fileInfo = { 0 };
    250                     fileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
    251                     fileInfo.pcwszFilePath = msixPath.c_str();
    252 
    253                     WINTRUST_DATA trustData = { 0 };
    254                     trustData.cbStruct = sizeof(WINTRUST_DATA);
    255                     trustData.dwUIChoice = WTD_UI_NONE;
    256                     trustData.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN;
    257                     trustData.dwUnionChoice = WTD_CHOICE_FILE;
    258                     trustData.dwStateAction = WTD_STATEACTION_VERIFY;
    259                     trustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
    260                     trustData.pFile = &fileInfo;
    261 
    262                     GUID verifyActionId = WINTRUST_ACTION_GENERIC_VERIFY_V2;
    263 
    264                     HRESULT verifyTrustResult = static_cast<HRESULT>(WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &verifyActionId, &trustData));
    265                     AICLI_LOG(Core, Info, << "Result for trust info validation of the msix: " << verifyTrustResult);
    266 
    267                     result = verifyTrustResult == S_OK;
    268                 }
    269             }
    270             catch (const wil::ResultException& re)
    271             {
    272                 AICLI_LOG(Core, Error, << "Failed during msix trust validation. Error: " << re.GetErrorCode());
    273                 result = false;
    274             }
    275             catch (...)
    276             {
    277                 AICLI_LOG(Core, Error, << "Failed during msix trust validation.");
    278                 result = false;
    279             }
    280 
    281             return result;
    282         }
    283     }
    284 
    285     bool GetBundleReader(
    286         IStream* inputStream,
    287         IAppxBundleReader** reader)
    288     {
    289         ComPtr<IAppxBundleFactory> bundleFactory;
    290 
    291         // Create a new Appxbundle factory
    292         THROW_IF_FAILED(CoCreateInstance(
    293             __uuidof(AppxBundleFactory),
    294             nullptr,
    295             CLSCTX_INPROC_SERVER,
    296             __uuidof(IAppxBundleFactory),
    297             (LPVOID*)(&bundleFactory)));
    298 
    299         HRESULT hr = bundleFactory->CreateBundleReader(inputStream, reader);
    300 
    301         if (SUCCEEDED(hr))
    302         {
    303             return true;
    304         }
    305         else if (hr == APPX_E_MISSING_REQUIRED_FILE)
    306         {
    307             // APPX_E_MISSING_REQUIRED_FILE returned when trying to open
    308             // an *.msix as an *.msixbundle or vice-versa.
    309             return false;
    310         }
    311         else
    312         {
    313             THROW_HR(hr);
    314         }
    315     }
    316 
    317     bool GetPackageReader(
    318         IStream* inputStream,
    319         IAppxPackageReader** reader)
    320     {
    321         ComPtr<IAppxFactory> appxFactory;
    322 
    323         // Create a new Appx factory
    324         THROW_IF_FAILED(CoCreateInstance(
    325             __uuidof(AppxFactory),
    326             nullptr,
    327             CLSCTX_INPROC_SERVER,
    328             __uuidof(IAppxFactory),
    329             (LPVOID*)(&appxFactory)));
    330 
    331         // Create a new package reader using the factory.
    332         HRESULT hr = appxFactory->CreatePackageReader(inputStream, reader);
    333 
    334         if (SUCCEEDED(hr))
    335         {
    336             return true;
    337         }
    338         else if (hr == APPX_E_MISSING_REQUIRED_FILE)
    339         {
    340             // APPX_E_MISSING_REQUIRED_FILE returned when trying to open
    341             // an *.msix as an *.msixbundle or vice-versa.
    342             return false;
    343         }
    344         else
    345         {
    346             THROW_HR(hr);
    347         }
    348     }
    349 
    350     void GetManifestReader(
    351         IStream* inputStream,
    352         IAppxManifestReader** reader)
    353     {
    354         ComPtr<IAppxFactory> appxFactory;
    355 
    356         THROW_IF_FAILED(CoCreateInstance(
    357             __uuidof(AppxFactory),
    358             nullptr,
    359             CLSCTX_INPROC_SERVER,
    360             __uuidof(IAppxFactory),
    361             (LPVOID*)(&appxFactory)));
    362 
    363         THROW_IF_FAILED(appxFactory->CreateManifestReader(inputStream, reader));
    364     }
    365 
    366     std::optional<std::string> GetPackageFullNameFromFamilyName(std::string_view familyName)
    367     {
    368         PackageManager packageManager;
    369 
    370         std::wstring pfn = Utility::ConvertToUTF16(familyName);
    371 
    372         // PackageManager.FindPackages() can find all packages (including provisioned ones) but requires admin.
    373         // For non admin callers, use FindPackagesByPackageFamily where only packages registered to current user will be found.
    374         if (Runtime::IsRunningAsAdmin())
    375         {
    376             auto packages = packageManager.FindPackages(pfn);
    377 
    378             std::optional<std::string> result;
    379             for (const auto& package : packages)
    380             {
    381                 if (result.has_value())
    382                 {
    383                     // More than 1 package found. Don't directly error, let caller deal with it.
    384                     AICLI_LOG(Core, Error, << "Multiple packages found for family name: " << familyName);
    385                     return {};
    386                 }
    387 
    388                 result = Utility::ConvertToUTF8(package.Id().FullName());
    389             }
    390 
    391             return result;
    392         }
    393         else
    394         {
    395             UINT32 fullNameCount = 0;
    396             UINT32 bufferLength = 0;
    397             UINT32 properties = 0;
    398             LONG findResult = FindPackagesByPackageFamily(pfn.c_str(), PACKAGE_FILTER_HEAD, &fullNameCount, nullptr, &bufferLength, nullptr, &properties);
    399             if (findResult == ERROR_SUCCESS || fullNameCount == 0)
    400             {
    401                 // No package found
    402                 return {};
    403             }
    404             else if (findResult != ERROR_INSUFFICIENT_BUFFER)
    405             {
    406                 THROW_WIN32(findResult);
    407             }
    408             else if (fullNameCount != 1)
    409             {
    410                 // Don't directly error, let caller deal with it
    411                 AICLI_LOG(Core, Error, << "Multiple packages found for family name: " << fullNameCount);
    412                 return {};
    413             }
    414 
    415             // fullNameCount == 1 at this point
    416             PWSTR fullNamePtr;
    417             std::wstring buffer(static_cast<size_t>(bufferLength) + 1, '\0');
    418             THROW_IF_WIN32_ERROR(FindPackagesByPackageFamily(pfn.c_str(), PACKAGE_FILTER_HEAD, &fullNameCount, &fullNamePtr, &bufferLength, &buffer[0], &properties));
    419             if (fullNameCount != 1 || bufferLength == 0)
    420             {
    421                 // Something changed in between, abandon
    422                 AICLI_LOG(Core, Error, << "Packages found for family name: " << fullNameCount);
    423                 return {};
    424             }
    425             buffer.resize(bufferLength - 1);
    426             return Utility::ConvertToUTF8(buffer);
    427         }
    428     }
    429 
    430     std::string GetPackageFamilyNameFromFullName(std::string_view fullName)
    431     {
    432         std::wstring result;
    433         result.resize(PACKAGE_FAMILY_NAME_MAX_LENGTH + 1);
    434         UINT32 size = static_cast<UINT32>(result.size());
    435         THROW_IF_WIN32_ERROR(PackageFamilyNameFromFullName(Utility::ConvertToUTF16(fullName).c_str(), &size, &result[0]));
    436         result.resize(size - 1);
    437         return Utility::ConvertToUTF8(result);
    438     }
    439 
    440     std::optional<std::filesystem::path> GetPackageLocationFromFullName(std::string_view fullName)
    441     {
    442         std::wstring fn = Utility::ConvertToUTF16(fullName);
    443 
    444         UINT32 length = 0;
    445         LONG returnVal = GetStagedPackagePathByFullName(fn.c_str(), &length, nullptr);
    446         if (returnVal != ERROR_INSUFFICIENT_BUFFER)
    447         {
    448             LOG_WIN32(returnVal);
    449             return {};
    450         }
    451 
    452         THROW_HR_IF(E_UNEXPECTED, length == 0);
    453 
    454         std::wstring result;
    455         result.resize(length);
    456 
    457         returnVal = GetStagedPackagePathByFullName(fn.c_str(), &length, &result[0]);
    458         if (returnVal != ERROR_SUCCESS)
    459         {
    460             LOG_WIN32(returnVal);
    461             return {};
    462         }
    463 
    464         result.resize(length - 1);
    465         return { result };
    466     }
    467 
    468     Msix::PackageIdInfo GetPackageIdInfoFromFullName(std::string_view fullName)
    469     {
    470         std::wstring fullNameWide = Utility::ConvertToUTF16(fullName);
    471 
    472         UINT32 length = 0;
    473         LONG returnVal = PackageIdFromFullName(fullNameWide.c_str(), PACKAGE_INFORMATION_BASIC, &length, nullptr);
    474         if (returnVal != ERROR_INSUFFICIENT_BUFFER)
    475         {
    476             LOG_WIN32(returnVal);
    477             return {};
    478         }
    479 
    480         THROW_HR_IF(E_UNEXPECTED, length == 0);
    481 
    482         std::unique_ptr<BYTE[]> packageIdContent = std::make_unique<BYTE[]>(length);
    483 
    484         returnVal = PackageIdFromFullName(fullNameWide.c_str(), PACKAGE_INFORMATION_BASIC, &length, packageIdContent.get());
    485         if (returnVal != ERROR_SUCCESS)
    486         {
    487             LOG_WIN32(returnVal);
    488             return {};
    489         }
    490 
    491         PACKAGE_ID* packageId = (PACKAGE_ID*)packageIdContent.get();
    492 
    493         return { Utility::ConvertToUTF8(packageId->name), packageId->version.Version };
    494     }
    495 
    496     GetCertContextResult GetCertContextFromMsix(const std::filesystem::path& msixPath)
    497     {
    498         // Retrieve raw signature from msix
    499         MsixInfo msixInfo{ msixPath };
    500         auto signature = msixInfo.GetSignature(true);
    501 
    502         // Get the cert content
    503         wil::unique_any<HCRYPTMSG, decltype(&::CryptMsgClose), ::CryptMsgClose> signedMessage;
    504         wil::unique_hcertstore certStore;
    505         CRYPT_DATA_BLOB signatureBlob = { 0 };
    506         signatureBlob.cbData = static_cast<DWORD>(signature.size());
    507         signatureBlob.pbData = signature.data();
    508         THROW_LAST_ERROR_IF(!CryptQueryObject(
    509             CERT_QUERY_OBJECT_BLOB,
    510             &signatureBlob,
    511             CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED,
    512             CERT_QUERY_FORMAT_FLAG_BINARY,
    513             0,      // Reserved parameter
    514             NULL,   // No encoding info needed
    515             NULL,
    516             NULL,
    517             &certStore,
    518             &signedMessage,
    519             NULL));
    520 
    521         // Get the signer size and information from the signed data message
    522         // The properties of the signer info will be used to uniquely identify the signing certificate in the certificate store
    523         DWORD signerInfoSize = 0;
    524         THROW_LAST_ERROR_IF(!CryptMsgGetParam(
    525             signedMessage.get(),
    526             CMSG_SIGNER_INFO_PARAM,
    527             0,
    528             NULL,
    529             &signerInfoSize));
    530 
    531         // Check that the signer info size is within reasonable bounds; under the max length of a string for the issuer field
    532         THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), !(signerInfoSize > 0 && signerInfoSize < STRSAFE_MAX_CCH));
    533 
    534         std::vector<byte> signerInfoBuffer;
    535         signerInfoBuffer.resize(signerInfoSize);
    536         THROW_LAST_ERROR_IF(!CryptMsgGetParam(
    537             signedMessage.get(),
    538             CMSG_SIGNER_INFO_PARAM,
    539             0,
    540             signerInfoBuffer.data(),
    541             &signerInfoSize));
    542 
    543         // Get the signing certificate from the certificate store based on the issuer and serial number of the signer info
    544         CMSG_SIGNER_INFO* signerInfo = reinterpret_cast<CMSG_SIGNER_INFO*>(signerInfoBuffer.data());
    545         CERT_INFO certInfo;
    546         certInfo.Issuer = signerInfo->Issuer;
    547         certInfo.SerialNumber = signerInfo->SerialNumber;
    548 
    549         wil::unique_cert_context certContext;
    550         certContext.reset(CertGetSubjectCertificateFromStore(
    551             certStore.get(),
    552             X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
    553             &certInfo));
    554         THROW_LAST_ERROR_IF(!certContext.get());
    555 
    556         return { std::move(certContext), std::move(certStore) };
    557     }
    558 
    559     MsixInfo::MsixInfo(std::string_view uriStr)
    560     {
    561         m_stream = Utility::GetReadOnlyStreamFromURI(uriStr);
    562 
    563         if (GetBundleReader(m_stream.Get(), &m_bundleReader))
    564         {
    565             m_isBundle = true;
    566         }
    567         else if (GetPackageReader(m_stream.Get(), &m_packageReader))
    568         {
    569             m_isBundle = false;
    570         }
    571         else
    572         {
    573             THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_INSTALL_OPEN_PACKAGE_FAILED),
    574                 "Failed to open uri as msix package or bundle. Uri: %hs", uriStr.data());
    575         }
    576     }
    577 
    578     std::vector<byte> MsixInfo::GetSignature(bool skipP7xFileId)
    579     {
    580         ComPtr<IAppxFile> signatureFile;
    581         if (m_isBundle)
    582         {
    583             THROW_IF_FAILED(m_bundleReader->GetFootprintFile(APPX_BUNDLE_FOOTPRINT_FILE_TYPE_SIGNATURE, &signatureFile));
    584         }
    585         else
    586         {
    587             THROW_IF_FAILED(m_packageReader->GetFootprintFile(APPX_FOOTPRINT_FILE_TYPE_SIGNATURE, &signatureFile));
    588         }
    589 
    590         std::vector<byte> signatureContent;
    591         DWORD signatureSize;
    592 
    593         ComPtr<IStream> signatureStream;
    594         THROW_IF_FAILED(signatureFile->GetStream(&signatureStream));
    595 
    596         STATSTG stat = { 0 };
    597         THROW_IF_FAILED(signatureStream->Stat(&stat, STATFLAG_NONAME));
    598         THROW_HR_IF(E_UNEXPECTED, stat.cbSize.HighPart != 0); // Signature size should be small
    599         signatureSize = stat.cbSize.LowPart;
    600         THROW_HR_IF(E_UNEXPECTED, signatureSize <= P7xFileIdSize);
    601 
    602         if (skipP7xFileId)
    603         {
    604             // Validate msix signature header
    605             byte headerBuffer[P7xFileIdSize];
    606             DWORD headerRead;
    607             THROW_IF_FAILED(signatureStream->Read(headerBuffer, P7xFileIdSize, &headerRead));
    608             THROW_HR_IF_MSG(E_UNEXPECTED, headerRead != P7xFileIdSize, "Failed to read signature header");
    609             THROW_HR_IF_MSG(E_UNEXPECTED, !std::equal(P7xFileId, P7xFileId + P7xFileIdSize, headerBuffer), "Unexpected msix signature header");
    610             signatureSize -= P7xFileIdSize;
    611         }
    612 
    613         signatureContent.resize(signatureSize);
    614 
    615         DWORD signatureRead;
    616         THROW_IF_FAILED(signatureStream->Read(signatureContent.data(), signatureSize, &signatureRead));
    617         THROW_HR_IF_MSG(E_UNEXPECTED, signatureRead != signatureSize, "Failed to read the whole signature stream");
    618 
    619         return signatureContent;
    620     }
    621 
    622     Utility::SHA256::HashBuffer MsixInfo::GetSignatureHash()
    623     {
    624         auto signature = GetSignature();
    625         return Utility::SHA256::ComputeHash(signature.data(), static_cast<uint32_t>(signature.size()));
    626     }
    627 
    628     std::wstring MsixInfo::GetDigest()
    629     {
    630         ComPtr<IAppxDigestProvider> digestProvider;
    631         if (m_isBundle)
    632         {
    633             THROW_IF_FAILED(m_bundleReader.As(&digestProvider));
    634         }
    635         else
    636         {
    637             THROW_IF_FAILED(m_packageReader.As(&digestProvider));
    638         }
    639 
    640         wil::unique_cotaskmem_string result;
    641         THROW_IF_FAILED(digestProvider->GetDigest(&result));
    642 
    643         return result.get();
    644     }
    645 
    646     std::wstring MsixInfo::GetPackageFullNameWide()
    647     {
    648         ComPtr<IAppxManifestPackageId> packageId;
    649         if (m_isBundle)
    650         {
    651             ComPtr<IAppxBundleManifestReader> manifestReader;
    652             THROW_IF_FAILED(m_bundleReader->GetManifest(&manifestReader));
    653             THROW_IF_FAILED(manifestReader->GetPackageId(&packageId));
    654         }
    655         else
    656         {
    657             ComPtr<IAppxManifestReader> manifestReader;
    658             THROW_IF_FAILED(m_packageReader->GetManifest(&manifestReader));
    659             THROW_IF_FAILED(manifestReader->GetPackageId(&packageId));
    660         }
    661 
    662         wil::unique_cotaskmem_string fullName;
    663         THROW_IF_FAILED(packageId->GetPackageFullName(&fullName));
    664 
    665         return { fullName.get() };
    666     }
    667 
    668     std::string MsixInfo::GetPackageFullName()
    669     {
    670         return Utility::ConvertToUTF8(GetPackageFullNameWide());
    671     }
    672 
    673     std::vector<ComPtr<IAppxPackageReader>> MsixInfo::GetAppPackages(bool includeStub) const
    674     {
    675         if (!m_isBundle)
    676         {
    677             return { m_packageReader };
    678         }
    679 
    680         std::vector<ComPtr<IAppxPackageReader>> packages;
    681 
    682         ComPtr<IAppxBundleManifestReader> manifestReader;
    683         THROW_IF_FAILED(m_bundleReader->GetManifest(&manifestReader));
    684 
    685         ComPtr<IAppxBundleManifestPackageInfoEnumerator> packageInfoItems;
    686         THROW_IF_FAILED(manifestReader->GetPackageInfoItems(&packageInfoItems));
    687 
    688         BOOL hasCurrent = FALSE;
    689         THROW_IF_FAILED(packageInfoItems->GetHasCurrent(&hasCurrent));
    690         while (hasCurrent)
    691         {
    692             ComPtr<IAppxBundleManifestPackageInfo> packageInfo;
    693             THROW_IF_FAILED(packageInfoItems->GetCurrent(&packageInfo));
    694 
    695             APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType;
    696             THROW_IF_FAILED(packageInfo->GetPackageType(&packageType));
    697 
    698             // Check flat bundle case.
    699             UINT64 offset;
    700             THROW_IF_FAILED(packageInfo->GetOffset(&offset));
    701             bool isContained = offset != 0;
    702 
    703             // Check stub package case.
    704             ComPtr<IAppxBundleManifestPackageInfo4> packageInfo4;
    705             THROW_IF_FAILED(packageInfo.As(&packageInfo4));
    706             BOOL isStub = FALSE;
    707             THROW_IF_FAILED(packageInfo4->GetIsStub(&isStub));
    708 
    709             if (isContained && (includeStub || !isStub) &&
    710                 packageType == APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE::APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION)
    711             {
    712                 wil::unique_cotaskmem_string fileName;
    713                 THROW_IF_FAILED(packageInfo->GetFileName(&fileName));
    714 
    715                 ComPtr<IAppxFile> packageFile;
    716                 THROW_IF_FAILED(m_bundleReader->GetPayloadPackage(fileName.get(), &packageFile));
    717 
    718                 ComPtr<IStream> stream;
    719                 THROW_IF_FAILED(packageFile->GetStream(&stream));
    720 
    721                 ComPtr<IAppxPackageReader> packageReader;
    722                 if (GetPackageReader(stream.Get(), &packageReader))
    723                 {
    724                     packages.emplace_back(std::move(packageReader));
    725                 }
    726                 else
    727                 {
    728                     AICLI_LOG(Core, Warning, << "Could not get package reader for bundle payload.");
    729                 }
    730             }
    731 
    732             THROW_IF_FAILED(packageInfoItems->MoveNext(&hasCurrent));
    733         }
    734 
    735         return packages;
    736     }
    737 
    738     std::vector<MsixPackageManifest> MsixInfo::GetAppPackageManifests(bool includeStub) const
    739     {
    740         std::vector<MsixPackageManifest> manifests;
    741         auto packages = GetAppPackages(includeStub);
    742         for (const auto& package : packages)
    743         {
    744             ComPtr<IAppxManifestReader> manifestReader;
    745             THROW_IF_FAILED(package->GetManifest(&manifestReader));
    746             manifests.emplace_back(std::move(manifestReader));
    747         }
    748 
    749         return manifests;
    750     }
    751 
    752     bool MsixInfo::IsNewerThan(const std::filesystem::path& otherPackage)
    753     {
    754         THROW_HR_IF(E_NOT_VALID_STATE, m_isBundle);
    755 
    756         MsixInfo other{ otherPackage };
    757 
    758         THROW_HR_IF(E_INVALIDARG, other.m_isBundle);
    759 
    760         ComPtr<IAppxManifestReader> otherReader;
    761         THROW_IF_FAILED(other.m_packageReader->GetManifest(&otherReader));
    762 
    763         ComPtr<IAppxManifestReader> manifestReader;
    764         THROW_IF_FAILED(m_packageReader->GetManifest(&manifestReader));
    765 
    766         return (GetVersionFromManifestReader(manifestReader.Get()) > GetVersionFromManifestReader(otherReader.Get()));
    767     }
    768 
    769     bool MsixInfo::IsNewerThan(const winrt::Windows::ApplicationModel::PackageVersion& otherVersion)
    770     {
    771         THROW_HR_IF(E_NOT_VALID_STATE, m_isBundle);
    772 
    773         ComPtr<IAppxManifestReader> manifestReader;
    774         THROW_IF_FAILED(m_packageReader->GetManifest(&manifestReader));
    775 
    776         return (GetVersionFromManifestReader(manifestReader.Get()) > GetVersionFromVersion(otherVersion));
    777     }
    778 
    779     void MsixInfo::WriteToFile(std::string_view packageFile, const std::filesystem::path& target, IProgressCallback& progress)
    780     {
    781         std::wstring fileUTF16 = Utility::ConvertToUTF16(packageFile);
    782 
    783         ComPtr<IAppxFile> appxFile;
    784         if (m_isBundle)
    785         {
    786             THROW_IF_FAILED(m_bundleReader->GetPayloadPackage(fileUTF16.c_str(), &appxFile));
    787         }
    788         else
    789         {
    790             THROW_IF_FAILED(m_packageReader->GetPayloadFile(fileUTF16.c_str(), &appxFile));
    791         }
    792 
    793         WriteAppxFileToFile(appxFile.Get(), target, progress);
    794     }
    795 
    796     void MsixInfo::WriteManifestToFile(const std::filesystem::path& target, IProgressCallback& progress)
    797     {
    798         ComPtr<IAppxFile> appxFile;
    799         if (m_isBundle)
    800         {
    801             THROW_IF_FAILED(m_bundleReader->GetFootprintFile(APPX_BUNDLE_FOOTPRINT_FILE_TYPE_MANIFEST, &appxFile));
    802         }
    803         else
    804         {
    805             THROW_IF_FAILED(m_packageReader->GetFootprintFile(APPX_FOOTPRINT_FILE_TYPE_MANIFEST, &appxFile));
    806         }
    807 
    808         WriteAppxFileToFile(appxFile.Get(), target, progress);
    809     }
    810 
    811     void MsixInfo::WriteToFileHandle(std::string_view packageFile, HANDLE target, IProgressCallback& progress)
    812     {
    813         std::wstring fileUTF16 = Utility::ConvertToUTF16(packageFile);
    814 
    815         ComPtr<IAppxFile> appxFile;
    816         if (m_isBundle)
    817         {
    818             THROW_IF_FAILED(m_bundleReader->GetPayloadPackage(fileUTF16.c_str(), &appxFile));
    819         }
    820         else
    821         {
    822             THROW_IF_FAILED(m_packageReader->GetPayloadFile(fileUTF16.c_str(), &appxFile));
    823         }
    824 
    825         WriteAppxFileToFileHandle(appxFile.Get(), target, progress);
    826     }
    827 
    828     WriteLockedMsixFile::WriteLockedMsixFile(const std::filesystem::path& path)
    829     {
    830         m_file = Utility::ManagedFile::OpenWriteLockedFile(path, 0);
    831     }
    832 
    833     bool WriteLockedMsixFile::ValidateTrustInfo(bool checkMicrosoftOrigin) const
    834     {
    835         return ValidateMsixTrustInfo(m_file.GetFilePath(), checkMicrosoftOrigin);
    836     }
    837 }