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 }