winget-cli

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

SHA256.cpp (7072B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include <pch.h>
      4 #define WIN32_NO_STATUS
      5 #include <bcrypt.h>
      6 #include "Public/AppInstallerSHA256.h"
      7 #include "Public/AppInstallerErrors.h"
      8 #include "Public/AppInstallerStrings.h"
      9 
     10 namespace AppInstaller::Utility {
     11 
     12     struct SHA256Context
     13     {
     14         wil::unique_bcrypt_algorithm algHandle;
     15         wil::unique_bcrypt_hash hashHandle;
     16         DWORD hashLength = 0;
     17     };
     18 
     19     SHA256::SHA256() : context(new SHA256Context{})
     20     {
     21         BCRYPT_ALG_HANDLE algHandleT{};
     22         BCRYPT_HASH_HANDLE hashHandleT;
     23         DWORD resultLength = 0;
     24 
     25         // Open an algorithm handle
     26         THROW_IF_NTSTATUS_FAILED_MSG(BCryptOpenAlgorithmProvider(
     27             &algHandleT,                // Alg Handle pointer
     28             BCRYPT_SHA256_ALGORITHM,    // Cryptographic Algorithm name (null terminated unicode string)
     29             nullptr,                    // Provider name; if null, the default provider is loaded
     30             0),                         // Flags
     31             "failed opening SHA256 algorithm provider");
     32         context->algHandle.reset(algHandleT);
     33 
     34         // Obtain the length of the hash
     35         THROW_IF_NTSTATUS_FAILED_MSG(BCryptGetProperty(
     36             context->algHandle.get(),       // Handle to a CNG object
     37             BCRYPT_HASH_LENGTH,             // Property name (null terminated unicode string)
     38             (PBYTE) & (context->hashLength),  // Address of the output buffer which receives the property value
     39             sizeof(context->hashLength),    // Size of the buffer in bytes
     40             &resultLength,                  // Number of bytes that were copied into the buffer
     41             0),                             // Flags
     42             "failed getting SHA256 hash length");
     43         
     44         if (resultLength != sizeof(context->hashLength))
     45         {
     46             THROW_HR_MSG(E_UNEXPECTED, "failed getting SHA256 hash length");
     47         }
     48 
     49         // Create a hash handle
     50         THROW_IF_NTSTATUS_FAILED_MSG(BCryptCreateHash(
     51             context->algHandle.get(),   // Handle to an algorithm provider
     52             &hashHandleT,               // A pointer to a hash handle - can be a hash or hmac object
     53             nullptr,                    // Pointer to the buffer that receives the hash/hmac object
     54             0,                          // Size of the buffer in bytes
     55             nullptr,                    // A pointer to a key to use for the hash or MAC
     56             0,                          // Size of the key in bytes
     57             0),                         // Flags
     58             "failed creating SHA256 hash object");
     59         context->hashHandle.reset(hashHandleT);
     60     }
     61 
     62     void SHA256::Add(const uint8_t* buffer, size_t cbBuffer)
     63     {
     64         EnsureNotFinished();
     65 
     66         // Add the data
     67         THROW_IF_NTSTATUS_FAILED_MSG(
     68             BCryptHashData(context->hashHandle.get(), const_cast<PUCHAR>(buffer), static_cast<ULONG>(cbBuffer), 0),
     69             "failed adding SHA256 data");
     70     }
     71 
     72     void SHA256::Get(HashBuffer& hash)
     73     {
     74         EnsureNotFinished();
     75 
     76         // Size the hash buffer appropriately
     77         hash.resize(context->hashLength);
     78 
     79         // Obtain the hash of the message(s) into the hash buffer
     80         THROW_IF_NTSTATUS_FAILED_MSG(BCryptFinishHash(
     81             context->hashHandle.get(),  // Handle to the hash or MAC object
     82             hash.data(),                // A pointer to a buffer that receives the hash or MAC value
     83             context->hashLength,        // Size of the buffer in bytes
     84             0),                         // Flags
     85             "failed getting SHA256 hash");
     86 
     87         context.reset();
     88     }
     89 
     90     std::string SHA256::ConvertToString(const HashBuffer& hashBuffer)
     91     {
     92         return Utility::ConvertToHexString(hashBuffer, HashBufferSizeInBytes);
     93     }
     94 
     95     std::wstring SHA256::ConvertToWideString(const HashBuffer& hashBuffer)
     96     {
     97         return ConvertToUTF16(SHA256::ConvertToString(hashBuffer));
     98     }
     99 
    100     SHA256::HashBuffer SHA256::ConvertToBytes(const std::string& hashStr)
    101     {
    102         return Utility::ParseFromHexString(hashStr, HashBufferSizeInBytes);
    103     }
    104 
    105     SHA256::HashBuffer SHA256::ComputeHash(const std::uint8_t* buffer, std::uint32_t cbBuffer)
    106     {
    107         SHA256 hasher;
    108         hasher.Add(buffer, cbBuffer);
    109         return hasher.Get();
    110     }
    111 
    112     SHA256::HashBuffer SHA256::ComputeHash(const std::vector<uint8_t>& buffer)
    113     {
    114         THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), buffer.size() > std::numeric_limits<uint32_t>::max());
    115         return ComputeHash(buffer.data(), static_cast<uint32_t>(buffer.size()));
    116     }
    117 
    118     SHA256::HashBuffer SHA256::ComputeHash(std::string_view buffer)
    119     {
    120         return ComputeHash(reinterpret_cast<const std::uint8_t*>(buffer.data()), static_cast<std::uint32_t>(buffer.size()));
    121     }
    122 
    123     SHA256::HashBuffer SHA256::ComputeHash(std::istream& in)
    124     {
    125         return ComputeHashDetails(in).Hash;
    126     }
    127 
    128     SHA256::HashDetails SHA256::ComputeHashDetails(std::istream& in)
    129     {
    130         // Throw exceptions on badbit
    131         auto excState = in.exceptions();
    132         auto revertExcState = wil::scope_exit([excState, &in]() { in.exceptions(excState); });
    133         in.exceptions(std::ios_base::badbit);
    134 
    135         const int bufferSize = 1024 * 1024; // 1MB
    136         auto buffer = std::make_unique<uint8_t[]>(bufferSize);
    137 
    138         SHA256 hasher;
    139         uint64_t totalSize = 0;
    140 
    141         while (in.good())
    142         {
    143             in.read((char*)(buffer.get()), bufferSize);
    144             std::streamsize bytesRead = in.gcount();
    145             if (bytesRead)
    146             {
    147                 hasher.Add(buffer.get(), static_cast<size_t>(bytesRead));
    148                 totalSize += static_cast<uint64_t>(bytesRead);
    149             }
    150         }
    151 
    152         if (in.eof())
    153         {
    154             HashDetails result;
    155             result.Hash = hasher.Get();
    156             result.SizeInBytes = totalSize;
    157             return result;
    158         }
    159         else
    160         {
    161             THROW_HR(APPINSTALLER_CLI_ERROR_STREAM_READ_FAILURE);
    162         }
    163     }
    164 
    165     SHA256::HashBuffer SHA256::ComputeHashFromFile(const std::filesystem::path& path)
    166     {
    167         std::ifstream inStream{ path, std::ifstream::binary };
    168         const Utility::SHA256::HashBuffer& targetFileHash = Utility::SHA256::ComputeHash(inStream);
    169         inStream.close();
    170         return targetFileHash;
    171     }
    172 
    173     void SHA256::SHA256ContextDeleter::operator()(SHA256Context* context)
    174     {
    175         delete context;
    176     }
    177 
    178     bool SHA256::AreEqual(const HashBuffer& first, const HashBuffer& second)
    179     {
    180         return (first.size() == second.size() && std::equal(first.begin(), first.end(), second.begin()));
    181     }
    182 
    183     void SHA256::EnsureNotFinished() const
    184     {
    185         if (!context)
    186         {
    187             THROW_HR_MSG(E_UNEXPECTED, "The hash is already finished");
    188         }
    189     }
    190 }