diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 8502f46a74..92b47f0f66 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -31,6 +31,8 @@ add_library(common Crypto/bn.h Crypto/ec.cpp Crypto/ec.h + Crypto/SHA1.cpp + Crypto/SHA1.h Debug/MemoryPatches.cpp Debug/MemoryPatches.h Debug/Threads.h diff --git a/Source/Core/Common/Crypto/SHA1.cpp b/Source/Core/Common/Crypto/SHA1.cpp new file mode 100644 index 0000000000..10bdb59741 --- /dev/null +++ b/Source/Core/Common/Crypto/SHA1.cpp @@ -0,0 +1,400 @@ +// Copyright 2017 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "SHA1.h" + +#include +#include + +#include + +#include "Common/Assert.h" +#include "Common/CPUDetect.h" +#include "Common/CommonTypes.h" +#include "Common/Swap.h" + +#ifdef _MSC_VER +#include +#else +#ifdef _M_X86_64 +#include +#elif defined(_M_ARM_64) +#if defined(__clang__) +// This is a bit of a hack to get clang to accept the sha1 intrinsics without modifying cmdline +// flags. Note __ARM_FEATURE_CRYPTO is deprecated and "SHA2" flag is the lowest one which includes +// SHA1. +#define __ARM_FEATURE_SHA2 +// ...needed for older clang before they made the switchover to more granular flags. +#define __ARM_FEATURE_CRYPTO +#endif +#include +#include +#endif +#endif + +#ifdef _MSC_VER +#define ATTRIBUTE_TARGET(x) +#else +#define ATTRIBUTE_TARGET(x) [[gnu::target(x)]] +#endif + +namespace Common::SHA1 +{ +class ContextMbed final : public Context +{ +public: + ContextMbed() + { + mbedtls_sha1_init(&ctx); + ASSERT(!mbedtls_sha1_starts_ret(&ctx)); + } + ~ContextMbed() { mbedtls_sha1_free(&ctx); } + virtual void Update(const u8* msg, size_t len) override + { + ASSERT(!mbedtls_sha1_update_ret(&ctx, msg, len)); + } + virtual Digest Finish() override + { + Digest digest; + ASSERT(!mbedtls_sha1_finish_ret(&ctx, digest.data())); + return digest; + } + +private: + mbedtls_sha1_context ctx{}; +}; + +class BlockContext : public Context +{ +protected: + static constexpr size_t BLOCK_LEN = 64; + static constexpr u32 K[4]{0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6}; + static constexpr u32 H[5]{0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0}; + + virtual void ProcessBlock(const u8* msg) = 0; + virtual Digest GetDigest() = 0; + + virtual void Update(const u8* msg, size_t len) override + { + if (len == 0) + return; + msg_len += len; + + if (block_used) + { + if (block_used + len >= block.size()) + { + size_t rem = block.size() - block_used; + std::memcpy(&block[block_used], msg, rem); + ProcessBlock(&block[0]); + block_used = 0; + msg += rem; + len -= rem; + } + else + { + std::memcpy(&block[block_used], msg, len); + block_used += len; + return; + } + } + while (len >= BLOCK_LEN) + { + ProcessBlock(msg); + msg += BLOCK_LEN; + len -= BLOCK_LEN; + } + if (len) + { + std::memcpy(&block[0], msg, len); + block_used = len; + } + } + + virtual Digest Finish() override + { + // block_used is guaranteed < BLOCK_LEN + block[block_used++] = 0x80; + + constexpr size_t MSG_LEN_POS = BLOCK_LEN - sizeof(u64); + if (block_used > MSG_LEN_POS) + { + // Pad current block and process it + std::memset(&block[block_used], 0, BLOCK_LEN - block_used); + ProcessBlock(&block[0]); + + // Pad a new block + std::memset(&block[0], 0, MSG_LEN_POS); + } + else + { + // Pad current block + std::memset(&block[block_used], 0, MSG_LEN_POS - block_used); + } + + Common::BigEndianValue msg_bitlen(msg_len * 8); + std::memcpy(&block[MSG_LEN_POS], &msg_bitlen, sizeof(msg_bitlen)); + + ProcessBlock(&block[0]); + + return GetDigest(); + } + + alignas(64) std::array block{}; + size_t block_used{}; + size_t msg_len{}; +}; + +template +class CyclicArray +{ +public: + inline ValueType operator[](size_t i) const { return data[i % Size]; } + inline ValueType& operator[](size_t i) { return data[i % Size]; } + constexpr size_t size() { return Size; } + +private: + std::array data; +}; + +#ifdef _M_X86_64 + +// Uses the dedicated SHA1 instructions. Normal SSE(AVX*) would be needed for parallel +// multi-message processing. While Dolphin could gain from such implementation, it requires the +// calling code to be modified and/or making the SHA1 implementation asynchronous so it can +// optimistically batch. +class ContextX64SHA1 final : public BlockContext +{ +public: + ContextX64SHA1() + { + state[0] = _mm_set_epi32(H[0], H[1], H[2], H[3]); + state[1] = _mm_set_epi32(H[4], 0, 0, 0); + } + +private: + using WorkBlock = CyclicArray<__m128i, 4>; + + ATTRIBUTE_TARGET("ssse3") + static inline __m128i byterev_16B(__m128i x) + { + return _mm_shuffle_epi8(x, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)); + } + + template + ATTRIBUTE_TARGET("sha") + static inline __m128i MsgSchedule(WorkBlock* wblock) + { + auto& w = *wblock; + // Update and return this location + auto& wx = w[I]; + // Do all the xors and rol(x,1) required for 4 rounds of msg schedule + wx = _mm_sha1msg1_epu32(wx, w[I + 1]); + wx = _mm_xor_si128(wx, w[I + 2]); + wx = _mm_sha1msg2_epu32(wx, w[I + 3]); + return wx; + } + + ATTRIBUTE_TARGET("sha") + virtual void ProcessBlock(const u8* msg) override + { + // There are 80 rounds with 4 bytes per round, giving 0x140 byte work space, but we can keep + // active state in just 0x40 bytes. + // see FIPS 180-4 6.1.3 Alternate Method for Computing a SHA-1 Message Digest + WorkBlock w; + auto msg_block = (const __m128i*)msg; + for (size_t i = 0; i < w.size(); i++) + w[i] = byterev_16B(_mm_loadu_si128(&msg_block[i])); + + // 0: abcd, 1: e + auto abcde = state; + + // Not sure of a (non-ugly) way to have constant-evaluated for-loop, so just rely on inlining. + // Problem is that sha1rnds4 requires imm8 arg, and first/last rounds have different behavior. + + // clang-format off + // E0 += MSG0, special case of "nexte", can do normal add + abcde[1] = _mm_sha1rnds4_epu32(abcde[0], _mm_add_epi32(abcde[1], w[0]), 0); + abcde[0] = _mm_sha1rnds4_epu32(abcde[1], _mm_sha1nexte_epu32(abcde[0], w[1]), 0); + abcde[1] = _mm_sha1rnds4_epu32(abcde[0], _mm_sha1nexte_epu32(abcde[1], w[2]), 0); + abcde[0] = _mm_sha1rnds4_epu32(abcde[1], _mm_sha1nexte_epu32(abcde[0], w[3]), 0); + abcde[1] = _mm_sha1rnds4_epu32(abcde[0], _mm_sha1nexte_epu32(abcde[1], MsgSchedule<4>(&w)), 0); + abcde[0] = _mm_sha1rnds4_epu32(abcde[1], _mm_sha1nexte_epu32(abcde[0], MsgSchedule<5>(&w)), 1); + abcde[1] = _mm_sha1rnds4_epu32(abcde[0], _mm_sha1nexte_epu32(abcde[1], MsgSchedule<6>(&w)), 1); + abcde[0] = _mm_sha1rnds4_epu32(abcde[1], _mm_sha1nexte_epu32(abcde[0], MsgSchedule<7>(&w)), 1); + abcde[1] = _mm_sha1rnds4_epu32(abcde[0], _mm_sha1nexte_epu32(abcde[1], MsgSchedule<8>(&w)), 1); + abcde[0] = _mm_sha1rnds4_epu32(abcde[1], _mm_sha1nexte_epu32(abcde[0], MsgSchedule<9>(&w)), 1); + abcde[1] = _mm_sha1rnds4_epu32(abcde[0], _mm_sha1nexte_epu32(abcde[1], MsgSchedule<10>(&w)), 2); + abcde[0] = _mm_sha1rnds4_epu32(abcde[1], _mm_sha1nexte_epu32(abcde[0], MsgSchedule<11>(&w)), 2); + abcde[1] = _mm_sha1rnds4_epu32(abcde[0], _mm_sha1nexte_epu32(abcde[1], MsgSchedule<12>(&w)), 2); + abcde[0] = _mm_sha1rnds4_epu32(abcde[1], _mm_sha1nexte_epu32(abcde[0], MsgSchedule<13>(&w)), 2); + abcde[1] = _mm_sha1rnds4_epu32(abcde[0], _mm_sha1nexte_epu32(abcde[1], MsgSchedule<14>(&w)), 2); + abcde[0] = _mm_sha1rnds4_epu32(abcde[1], _mm_sha1nexte_epu32(abcde[0], MsgSchedule<15>(&w)), 3); + abcde[1] = _mm_sha1rnds4_epu32(abcde[0], _mm_sha1nexte_epu32(abcde[1], MsgSchedule<16>(&w)), 3); + abcde[0] = _mm_sha1rnds4_epu32(abcde[1], _mm_sha1nexte_epu32(abcde[0], MsgSchedule<17>(&w)), 3); + abcde[1] = _mm_sha1rnds4_epu32(abcde[0], _mm_sha1nexte_epu32(abcde[1], MsgSchedule<18>(&w)), 3); + abcde[0] = _mm_sha1rnds4_epu32(abcde[1], _mm_sha1nexte_epu32(abcde[0], MsgSchedule<19>(&w)), 3); + // state += abcde + state[1] = _mm_sha1nexte_epu32(abcde[1], state[1]); + state[0] = _mm_add_epi32(abcde[0], state[0]); + // clang-format on + } + + virtual Digest GetDigest() override + { + Digest digest; + _mm_storeu_si128((__m128i*)&digest[0], byterev_16B(state[0])); + u32 hi = _mm_cvtsi128_si32(byterev_16B(state[1])); + std::memcpy(&digest[sizeof(__m128i)], &hi, sizeof(hi)); + return digest; + } + + std::array<__m128i, 2> state{}; +}; + +#endif + +#ifdef _M_ARM_64 + +// The armv8 flags are very annoying: +// clang inserts "+" prefixes itself, gcc does not. +// clang has deprecated "crypto" (removed in clang 13), gcc has not. +#ifdef _MSC_VER +#define TARGET_ARMV8_SHA1 +#elif defined(__clang__) +#define TARGET_ARMV8_SHA1 [[gnu::target("sha2")]] +#elif defined(__GNUC__) +#define TARGET_ARMV8_SHA1 [[gnu::target("+crypto")]] +#endif + +class ContextNeon final : public BlockContext +{ +public: + ContextNeon() + { + state.abcd = vld1q_u32(&H[0]); + state.e = H[4]; + } + +private: + using WorkBlock = CyclicArray; + + struct State + { + // ARM thought they were being clever by exposing e as u32, but it actually makes non-asm + // implementations pretty annoying/makes compiler's life needlessly difficult. + uint32x4_t abcd{}; + u32 e{}; + }; + + static inline uint32x4_t byterev_16B(uint32x4_t x) + { + // Just rev32 with casting wrappers + return vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(x))); + } + + TARGET_ARMV8_SHA1 + static inline uint32x4_t MsgSchedule(WorkBlock* wblock, size_t i) + { + auto& w = *wblock; + // Update and return this location + auto& wx = w[0 + i]; + wx = vsha1su0q_u32(wx, w[1 + i], w[2 + i]); + wx = vsha1su1q_u32(wx, w[3 + i]); + return wx; + } + + template + TARGET_ARMV8_SHA1 static inline constexpr uint32x4_t f(State state, uint32x4_t w) + { + const auto wk = vaddq_u32(w, vdupq_n_u32(K[Func])); + if constexpr (Func == 0) + return vsha1cq_u32(state.abcd, state.e, wk); + if constexpr (Func == 1 || Func == 3) + return vsha1pq_u32(state.abcd, state.e, wk); + if constexpr (Func == 2) + return vsha1mq_u32(state.abcd, state.e, wk); + } + + template + TARGET_ARMV8_SHA1 static inline constexpr State FourRounds(State state, uint32x4_t w) + { +#ifdef _MSC_VER + // FIXME it seems the msvc optimizer gets a little too happy + _ReadBarrier(); +#endif + return {f(state, w), vsha1h_u32(vgetq_lane_u32(state.abcd, 0))}; + } + + virtual void ProcessBlock(const u8* msg) override + { + WorkBlock w; + for (size_t i = 0; i < w.size(); i++) + w[i] = byterev_16B(vld1q_u8(&msg[sizeof(uint32x4_t) * i])); + + std::array states{state}; + + // Fashioned to look like x64 impl. + // In each case the goal is to have compiler inline + unroll everything. + states[1] = FourRounds<0>(states[0], w[0]); + states[0] = FourRounds<0>(states[1], w[1]); + states[1] = FourRounds<0>(states[0], w[2]); + states[0] = FourRounds<0>(states[1], w[3]); + states[1] = FourRounds<0>(states[0], MsgSchedule(&w, 4)); + states[0] = FourRounds<1>(states[1], MsgSchedule(&w, 5)); + states[1] = FourRounds<1>(states[0], MsgSchedule(&w, 6)); + states[0] = FourRounds<1>(states[1], MsgSchedule(&w, 7)); + states[1] = FourRounds<1>(states[0], MsgSchedule(&w, 8)); + states[0] = FourRounds<1>(states[1], MsgSchedule(&w, 9)); + states[1] = FourRounds<2>(states[0], MsgSchedule(&w, 10)); + states[0] = FourRounds<2>(states[1], MsgSchedule(&w, 11)); + states[1] = FourRounds<2>(states[0], MsgSchedule(&w, 12)); + states[0] = FourRounds<2>(states[1], MsgSchedule(&w, 13)); + states[1] = FourRounds<2>(states[0], MsgSchedule(&w, 14)); + states[0] = FourRounds<3>(states[1], MsgSchedule(&w, 15)); + states[1] = FourRounds<3>(states[0], MsgSchedule(&w, 16)); + states[0] = FourRounds<3>(states[1], MsgSchedule(&w, 17)); + states[1] = FourRounds<3>(states[0], MsgSchedule(&w, 18)); + states[0] = FourRounds<3>(states[1], MsgSchedule(&w, 19)); + + state = {vaddq_u32(state.abcd, states[0].abcd), state.e + states[0].e}; + } + + virtual Digest GetDigest() override + { + Digest digest; + vst1q_u8(&digest[0], byterev_16B(state.abcd)); + u32 e = Common::FromBigEndian(state.e); + std::memcpy(&digest[sizeof(state.abcd)], &e, sizeof(e)); + return digest; + } + + State state; +}; + +#endif + +std::unique_ptr CreateContext() +{ + if (cpu_info.bSHA1) + { +#ifdef _M_X86_64 + // Note: As of mid 2022, > 99% of CPUs reporting to Steam survey have SSSE3, ~40% have SHA. + // Seems unlikely we'll see any cpus supporting SHA but not SSSE3 (in the foreseeable future at + // least). + if (cpu_info.bSSSE3) + return std::make_unique(); +#elif defined(_M_ARM_64) + return std::make_unique(); +#endif + } + return std::make_unique(); +} + +Digest CalculateDigest(const u8* msg, size_t len) +{ + auto ctx = CreateContext(); + ctx->Update(msg, len); + return ctx->Finish(); +} +} // namespace Common::SHA1 diff --git a/Source/Core/Common/Crypto/SHA1.h b/Source/Core/Common/Crypto/SHA1.h new file mode 100644 index 0000000000..9bb188494e --- /dev/null +++ b/Source/Core/Common/Crypto/SHA1.h @@ -0,0 +1,53 @@ +// Copyright 2017 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "Common/Assert.h" +#include "Common/CommonTypes.h" + +namespace Common::SHA1 +{ +using Digest = std::array; +static constexpr size_t DIGEST_LEN = sizeof(Digest); + +class Context +{ +public: + virtual ~Context() = default; + virtual void Update(const u8* msg, size_t len) = 0; + void Update(const std::vector& msg) { return Update(msg.data(), msg.size()); } + virtual Digest Finish() = 0; +}; + +std::unique_ptr CreateContext(); + +Digest CalculateDigest(const u8* msg, size_t len); + +template +inline Digest CalculateDigest(const std::vector& msg) +{ + static_assert(std::is_trivially_copyable_v); + ASSERT(std::numeric_limits::max() / sizeof(T) >= msg.size()); + return CalculateDigest(reinterpret_cast(msg.data()), sizeof(T) * msg.size()); +} + +inline Digest CalculateDigest(const std::string_view& msg) +{ + return CalculateDigest(reinterpret_cast(msg.data()), msg.size()); +} + +template +inline Digest CalculateDigest(const std::array& msg) +{ + static_assert(std::is_trivially_copyable_v); + return CalculateDigest(reinterpret_cast(msg.data()), sizeof(msg)); +} +} // namespace Common::SHA1 diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index 42eb7febe1..cd481cd426 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -10,7 +10,6 @@ #include #include -#include #if defined(_WIN32) #include @@ -25,6 +24,7 @@ #include "Common/CPUDetect.h" #include "Common/CommonTypes.h" #include "Common/Config/Config.h" +#include "Common/Crypto/SHA1.h" #include "Common/Random.h" #include "Common/Timer.h" #include "Common/Version.h" @@ -100,9 +100,8 @@ void DolphinAnalytics::GenerateNewIdentity() std::string DolphinAnalytics::MakeUniqueId(std::string_view data) const { - std::array digest; const auto input = std::string{m_unique_id}.append(data); - mbedtls_sha1_ret(reinterpret_cast(input.c_str()), input.size(), digest.data()); + const auto digest = Common::SHA1::CalculateDigest(input); // Convert to hex string and truncate to 64 bits. std::string out; diff --git a/Source/Core/Core/HW/GBACore.cpp b/Source/Core/Core/HW/GBACore.cpp index 54da03092e..d60e1308ad 100644 --- a/Source/Core/Core/HW/GBACore.cpp +++ b/Source/Core/Core/HW/GBACore.cpp @@ -3,8 +3,6 @@ #include "Core/HW/GBACore.h" -#include - #define PYCPARSE // Remove static functions from the header #include #undef PYCPARSE @@ -20,6 +18,7 @@ #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" #include "Common/Config/Config.h" +#include "Common/Crypto/SHA1.h" #include "Common/FileUtil.h" #include "Common/IOFile.h" #include "Common/MinizipUtil.h" @@ -143,11 +142,10 @@ static std::array GetROMHash(VFile* rom) size_t size = rom->size(rom); u8* buffer = static_cast(rom->map(rom, size, MAP_READ)); - std::array hash; - mbedtls_sha1_ret(buffer, size, hash.data()); + const auto digest = Common::SHA1::CalculateDigest(buffer, size); rom->unmap(rom, buffer, size); - return hash; + return digest; } Core::Core(int device_number) : m_device_number(device_number) diff --git a/Source/Core/Core/HW/WiiSave.cpp b/Source/Core/Core/HW/WiiSave.cpp index 62bbf9e7e0..57cb003aee 100644 --- a/Source/Core/Core/HW/WiiSave.cpp +++ b/Source/Core/Core/HW/WiiSave.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -24,6 +23,7 @@ #include "Common/Align.h" #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Common/Crypto/ec.h" #include "Common/FileUtil.h" #include "Common/IOFile.h" @@ -439,14 +439,14 @@ private: return false; // Read data to sign. - std::array data_sha1; + Common::SHA1::Digest data_sha1; { const u32 data_size = bk_header->size_of_files + sizeof(BkHeader); auto data = std::make_unique(data_size); m_file.Seek(sizeof(Header), File::SeekOrigin::Begin); if (!m_file.ReadBytes(data.get(), data_size)) return false; - mbedtls_sha1_ret(data.get(), data_size, data_sha1.data()); + data_sha1 = Common::SHA1::CalculateDigest(data.get(), data_size); } // Sign the data. diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index 84dc8b150d..7adb86e772 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -14,11 +14,11 @@ #include #include -#include #include "Common/Assert.h" #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Common/Logging/Log.h" #include "Common/NandPaths.h" #include "Common/StringUtil.h" @@ -109,10 +109,8 @@ static size_t GetIssuerOffset(SignatureType signature_type) std::array SignedBlobReader::GetSha1() const { - std::array sha1; const size_t skip = GetIssuerOffset(GetSignatureType()); - mbedtls_sha1_ret(m_bytes.data() + skip, m_bytes.size() - skip, sha1.data()); - return sha1; + return Common::SHA1::CalculateDigest(m_bytes.data() + skip, m_bytes.size() - skip); } bool SignedBlobReader::IsSignatureValid() const diff --git a/Source/Core/Core/IOS/ES/Identity.cpp b/Source/Core/Core/IOS/ES/Identity.cpp index c553958d46..1fbfec85ee 100644 --- a/Source/Core/Core/IOS/ES/Identity.cpp +++ b/Source/Core/Core/IOS/ES/Identity.cpp @@ -5,8 +5,7 @@ #include -#include - +#include "Common/Crypto/SHA1.h" #include "Common/Crypto/ec.h" #include "Common/Logging/Log.h" #include "Common/ScopeGuard.h" @@ -171,9 +170,8 @@ ReturnCode ESDevice::VerifySign(const std::vector& hash, const std::vector sha1; - mbedtls_sha1_ret(hash.data(), hash.size(), sha1.data()); - ret = iosc.VerifyPublicKeySign(sha1, ap_cert, ecc_signature, PID_ES); + const auto hash_digest = Common::SHA1::CalculateDigest(hash); + ret = iosc.VerifyPublicKeySign(hash_digest, ap_cert, ecc_signature, PID_ES); if (ret != IPC_SUCCESS) { ERROR_LOG_FMT(IOS_ES, "VerifySign: IOSC_VerifyPublicKeySign(data) failed with error {}", ret); diff --git a/Source/Core/Core/IOS/ES/NandUtils.cpp b/Source/Core/Core/IOS/ES/NandUtils.cpp index af0b17f993..f598ab2f99 100644 --- a/Source/Core/Core/IOS/ES/NandUtils.cpp +++ b/Source/Core/Core/IOS/ES/NandUtils.cpp @@ -12,9 +12,9 @@ #include #include -#include #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Common/Logging/Log.h" #include "Common/NandPaths.h" #include "Common/ScopeGuard.h" @@ -197,9 +197,7 @@ ESDevice::GetStoredContentsFromTMD(const ES::TMDReader& tmd, std::vector content_data(file->GetStatus()->size); if (!file->Read(content_data.data(), content_data.size())) return false; - std::array sha1{}; - mbedtls_sha1_ret(content_data.data(), content_data.size(), sha1.data()); - return sha1 == content.sha1; + return Common::SHA1::CalculateDigest(content_data) == content.sha1; }); return stored_contents; diff --git a/Source/Core/Core/IOS/ES/TitleManagement.cpp b/Source/Core/Core/IOS/ES/TitleManagement.cpp index 2169185064..216e54855d 100644 --- a/Source/Core/Core/IOS/ES/TitleManagement.cpp +++ b/Source/Core/Core/IOS/ES/TitleManagement.cpp @@ -9,9 +9,9 @@ #include #include -#include #include "Common/Align.h" +#include "Common/Crypto/SHA1.h" #include "Common/Logging/Log.h" #include "Common/NandPaths.h" #include "Core/CommonTitles.h" @@ -353,9 +353,7 @@ IPCReply ESDevice::ImportContentData(Context& context, const IOCtlVRequest& requ static bool CheckIfContentHashMatches(const std::vector& content, const ES::Content& info) { - std::array sha1; - mbedtls_sha1_ret(content.data(), info.size, sha1.data()); - return sha1 == info.sha1; + return Common::SHA1::CalculateDigest(content.data(), info.size) == info.sha1; } static std::string GetImportContentPath(u64 title_id, u32 content_id) diff --git a/Source/Core/Core/IOS/IOSC.cpp b/Source/Core/Core/IOS/IOSC.cpp index d9dc0f1fb2..8ec6af4269 100644 --- a/Source/Core/Core/IOS/IOSC.cpp +++ b/Source/Core/Core/IOS/IOSC.cpp @@ -14,11 +14,11 @@ #include #include #include -#include #include "Common/Assert.h" #include "Common/ChunkFile.h" #include "Common/Crypto/AES.h" +#include "Common/Crypto/SHA1.h" #include "Common/Crypto/ec.h" #include "Common/FileUtil.h" #include "Common/IOFile.h" @@ -249,8 +249,7 @@ ReturnCode IOSC::ComputeSharedKey(Handle dest_handle, Handle private_handle, Han const std::array shared_secret = Common::ec::ComputeSharedSecret(private_entry->data.data(), public_entry->data.data()); - std::array sha1; - mbedtls_sha1_ret(shared_secret.data(), shared_secret.size() / 2, sha1.data()); + const auto sha1 = Common::SHA1::CalculateDigest(shared_secret.data(), shared_secret.size() / 2); dest_entry->data.resize(AES128_KEY_SIZE); std::copy_n(sha1.cbegin(), AES128_KEY_SIZE, dest_entry->data.begin()); @@ -437,7 +436,6 @@ CertECC IOSC::GetDeviceCertificate() const void IOSC::Sign(u8* sig_out, u8* ap_cert_out, u64 title_id, const u8* data, u32 data_size) const { - std::array hash{}; std::array ap_priv{}; ap_priv[0x1d] = 1; @@ -451,13 +449,15 @@ void IOSC::Sign(u8* sig_out, u8* ap_cert_out, u64 title_id, const u8* data, u32 CertECC cert = MakeBlankEccCert(signer, name, ap_priv.data(), 0); // Sign the AP cert. const size_t skip = offsetof(CertECC, signature.issuer); - mbedtls_sha1_ret(reinterpret_cast(&cert) + skip, sizeof(cert) - skip, hash.data()); - cert.signature.sig = Common::ec::Sign(m_key_entries[HANDLE_CONSOLE_KEY].data.data(), hash.data()); + const auto ap_cert_digest = + Common::SHA1::CalculateDigest(reinterpret_cast(&cert) + skip, sizeof(cert) - skip); + cert.signature.sig = + Common::ec::Sign(m_key_entries[HANDLE_CONSOLE_KEY].data.data(), ap_cert_digest.data()); std::memcpy(ap_cert_out, &cert, sizeof(cert)); // Sign the data. - mbedtls_sha1_ret(data, data_size, hash.data()); - const auto signature = Common::ec::Sign(ap_priv.data(), hash.data()); + const auto data_digest = Common::SHA1::CalculateDigest(data, data_size); + const auto signature = Common::ec::Sign(ap_priv.data(), data_digest.data()); std::copy(signature.cbegin(), signature.cend(), sig_out); } diff --git a/Source/Core/DiscIO/Volume.cpp b/Source/Core/DiscIO/Volume.cpp index bf7ef4e019..b6d84254f0 100644 --- a/Source/Core/DiscIO/Volume.cpp +++ b/Source/Core/DiscIO/Volume.cpp @@ -12,9 +12,8 @@ #include #include -#include - #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Common/StringUtil.h" #include "Core/IOS/ES/Formats.h" @@ -33,21 +32,21 @@ const IOS::ES::TMDReader Volume::INVALID_TMD{}; const std::vector Volume::INVALID_CERT_CHAIN{}; template -static void AddToSyncHash(mbedtls_sha1_context* context, const T& data) +static void AddToSyncHash(Common::SHA1::Context* context, const T& data) { static_assert(std::is_trivially_copyable_v); - mbedtls_sha1_update_ret(context, reinterpret_cast(&data), sizeof(data)); + context->Update(reinterpret_cast(&data), sizeof(data)); } -void Volume::ReadAndAddToSyncHash(mbedtls_sha1_context* context, u64 offset, u64 length, +void Volume::ReadAndAddToSyncHash(Common::SHA1::Context* context, u64 offset, u64 length, const Partition& partition) const { std::vector buffer(length); if (Read(offset, length, buffer.data(), partition)) - mbedtls_sha1_update_ret(context, buffer.data(), buffer.size()); + context->Update(buffer); } -void Volume::AddTMDToSyncHash(mbedtls_sha1_context* context, const Partition& partition) const +void Volume::AddTMDToSyncHash(Common::SHA1::Context* context, const Partition& partition) const { // We want to hash some important parts of the TMD, but nothing that changes when fakesigning. // (Fakesigned WADs are very popular, and we don't want people with properly signed WADs to diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index 574afd6f83..2c974f447d 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -11,9 +11,8 @@ #include #include -#include - #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Common/StringUtil.h" #include "Common/Swap.h" #include "Core/IOS/ES/Formats.h" @@ -164,9 +163,9 @@ protected: return CP1252ToUTF8(string); } - void ReadAndAddToSyncHash(mbedtls_sha1_context* context, u64 offset, u64 length, + void ReadAndAddToSyncHash(Common::SHA1::Context* context, u64 offset, u64 length, const Partition& partition) const; - void AddTMDToSyncHash(mbedtls_sha1_context* context, const Partition& partition) const; + void AddTMDToSyncHash(Common::SHA1::Context* context, const Partition& partition) const; virtual u32 GetOffsetShift() const { return 0; } static std::map ReadWiiNames(const std::vector& data); diff --git a/Source/Core/DiscIO/VolumeDisc.cpp b/Source/Core/DiscIO/VolumeDisc.cpp index 273ccc4efa..b32ddc6d17 100644 --- a/Source/Core/DiscIO/VolumeDisc.cpp +++ b/Source/Core/DiscIO/VolumeDisc.cpp @@ -8,9 +8,8 @@ #include #include -#include - #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "DiscIO/DiscUtils.h" #include "DiscIO/Enums.h" #include "DiscIO/Filesystem.h" @@ -95,7 +94,7 @@ bool VolumeDisc::IsNKit() const return ReadSwapped(0x200, PARTITION_NONE) == NKIT_MAGIC; } -void VolumeDisc::AddGamePartitionToSyncHash(mbedtls_sha1_context* context) const +void VolumeDisc::AddGamePartitionToSyncHash(Common::SHA1::Context* context) const { const Partition partition = GetGamePartition(); diff --git a/Source/Core/DiscIO/VolumeDisc.h b/Source/Core/DiscIO/VolumeDisc.h index fc4964703f..17c76ae689 100644 --- a/Source/Core/DiscIO/VolumeDisc.h +++ b/Source/Core/DiscIO/VolumeDisc.h @@ -6,9 +6,8 @@ #include #include -#include - #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "DiscIO/Volume.h" namespace DiscIO @@ -27,7 +26,7 @@ public: protected: Region RegionCodeToRegion(std::optional region_code) const; - void AddGamePartitionToSyncHash(mbedtls_sha1_context* context) const; + void AddGamePartitionToSyncHash(Common::SHA1::Context* context) const; }; } // namespace DiscIO diff --git a/Source/Core/DiscIO/VolumeGC.cpp b/Source/Core/DiscIO/VolumeGC.cpp index 946364b4f7..8279b84820 100644 --- a/Source/Core/DiscIO/VolumeGC.cpp +++ b/Source/Core/DiscIO/VolumeGC.cpp @@ -11,11 +11,10 @@ #include #include -#include - #include "Common/Assert.h" #include "Common/ColorUtil.h" #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" @@ -152,15 +151,11 @@ bool VolumeGC::IsDatelDisc() const std::array VolumeGC::GetSyncHash() const { - mbedtls_sha1_context context; - mbedtls_sha1_init(&context); - mbedtls_sha1_starts_ret(&context); + auto context = Common::SHA1::CreateContext(); - AddGamePartitionToSyncHash(&context); + AddGamePartitionToSyncHash(context.get()); - std::array hash; - mbedtls_sha1_finish_ret(&context, hash.data()); - return hash; + return context->Finish(); } VolumeGC::ConvertedGCBanner VolumeGC::LoadBannerFile() const diff --git a/Source/Core/DiscIO/VolumeVerifier.cpp b/Source/Core/DiscIO/VolumeVerifier.cpp index 16033c56d4..33828382a9 100644 --- a/Source/Core/DiscIO/VolumeVerifier.cpp +++ b/Source/Core/DiscIO/VolumeVerifier.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include @@ -21,6 +20,7 @@ #include "Common/Assert.h" #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Common/FileUtil.h" #include "Common/Hash.h" #include "Common/HttpRequest.h" @@ -1053,8 +1053,7 @@ void VolumeVerifier::SetUpHashing() if (m_hashes_to_calculate.sha1) { - mbedtls_sha1_init(&m_sha1_context); - mbedtls_sha1_starts_ret(&m_sha1_context); + m_sha1_context = Common::SHA1::CreateContext(); } } @@ -1191,7 +1190,7 @@ void VolumeVerifier::Process() if (m_hashes_to_calculate.sha1) { m_sha1_future = std::async(std::launch::async, [this, byte_increment] { - mbedtls_sha1_update_ret(&m_sha1_context, m_data.data(), byte_increment); + m_sha1_context->Update(m_data.data(), byte_increment); }); } } @@ -1283,8 +1282,8 @@ void VolumeVerifier::Finish() if (m_hashes_to_calculate.sha1) { - m_result.hashes.sha1 = std::vector(20); - mbedtls_sha1_finish_ret(&m_sha1_context, m_result.hashes.sha1.data()); + const auto digest = m_sha1_context->Finish(); + m_result.hashes.sha1 = std::vector(digest.begin(), digest.end()); } } diff --git a/Source/Core/DiscIO/VolumeVerifier.h b/Source/Core/DiscIO/VolumeVerifier.h index 1a38f610b9..79c45c508e 100644 --- a/Source/Core/DiscIO/VolumeVerifier.h +++ b/Source/Core/DiscIO/VolumeVerifier.h @@ -5,14 +5,15 @@ #include #include +#include #include #include #include #include -#include #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Core/IOS/ES/Formats.h" #include "DiscIO/DiscScrubber.h" #include "DiscIO/Volume.h" @@ -174,7 +175,7 @@ private: bool m_calculating_any_hash = false; u32 m_crc32_context = 0; mbedtls_md5_context m_md5_context{}; - mbedtls_sha1_context m_sha1_context{}; + std::unique_ptr m_sha1_context; u64 m_excess_bytes = 0; std::vector m_data; diff --git a/Source/Core/DiscIO/VolumeWad.cpp b/Source/Core/DiscIO/VolumeWad.cpp index df12eab293..b485899c6e 100644 --- a/Source/Core/DiscIO/VolumeWad.cpp +++ b/Source/Core/DiscIO/VolumeWad.cpp @@ -14,11 +14,11 @@ #include #include -#include #include "Common/Align.h" #include "Common/Assert.h" #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" @@ -171,9 +171,7 @@ bool VolumeWAD::CheckContentIntegrity(const IOS::ES::Content& content, mbedtls_aes_crypt_cbc(&context, MBEDTLS_AES_DECRYPT, decrypted_data.size(), iv.data(), encrypted_data.data(), decrypted_data.data()); - std::array sha1; - mbedtls_sha1_ret(decrypted_data.data(), content.size, sha1.data()); - return sha1 == content.sha1; + return Common::SHA1::CalculateDigest(decrypted_data.data(), content.size) == content.sha1; } bool VolumeWAD::CheckContentIntegrity(const IOS::ES::Content& content, u64 content_offset, @@ -349,17 +347,13 @@ std::array VolumeWAD::GetSyncHash() const // We can skip hashing the contents since the TMD contains hashes of the contents. // We specifically don't hash the ticket, since its console ID can differ without any problems. - mbedtls_sha1_context context; - mbedtls_sha1_init(&context); - mbedtls_sha1_starts_ret(&context); + auto context = Common::SHA1::CreateContext(); - AddTMDToSyncHash(&context, PARTITION_NONE); + AddTMDToSyncHash(context.get(), PARTITION_NONE); - ReadAndAddToSyncHash(&context, m_opening_bnr_offset, m_opening_bnr_size, PARTITION_NONE); + ReadAndAddToSyncHash(context.get(), m_opening_bnr_offset, m_opening_bnr_size, PARTITION_NONE); - std::array hash; - mbedtls_sha1_finish_ret(&context, hash.data()); - return hash; + return context->Finish(); } } // namespace DiscIO diff --git a/Source/Core/DiscIO/VolumeWii.cpp b/Source/Core/DiscIO/VolumeWii.cpp index 93287093d5..ca421cd591 100644 --- a/Source/Core/DiscIO/VolumeWii.cpp +++ b/Source/Core/DiscIO/VolumeWii.cpp @@ -17,11 +17,11 @@ #include #include -#include #include "Common/Align.h" #include "Common/Assert.h" #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Common/Logging/Log.h" #include "Common/Swap.h" @@ -366,29 +366,25 @@ const BlobReader& VolumeWii::GetBlobReader() const std::array VolumeWii::GetSyncHash() const { - mbedtls_sha1_context context; - mbedtls_sha1_init(&context); - mbedtls_sha1_starts_ret(&context); + auto context = Common::SHA1::CreateContext(); // Disc header - ReadAndAddToSyncHash(&context, 0, 0x80, PARTITION_NONE); + ReadAndAddToSyncHash(context.get(), 0, 0x80, PARTITION_NONE); // Region code - ReadAndAddToSyncHash(&context, 0x4E000, 4, PARTITION_NONE); + ReadAndAddToSyncHash(context.get(), 0x4E000, 4, PARTITION_NONE); // The data offset of the game partition - an important factor for disc drive timings const u64 data_offset = PartitionOffsetToRawOffset(0, GetGamePartition()); - mbedtls_sha1_update_ret(&context, reinterpret_cast(&data_offset), sizeof(data_offset)); + context->Update(reinterpret_cast(&data_offset), sizeof(data_offset)); // TMD - AddTMDToSyncHash(&context, GetGamePartition()); + AddTMDToSyncHash(context.get(), GetGamePartition()); // Game partition contents - AddGamePartitionToSyncHash(&context); + AddGamePartitionToSyncHash(context.get()); - std::array hash; - mbedtls_sha1_finish_ret(&context, hash.data()); - return hash; + return context->Finish(); } bool VolumeWii::CheckH3TableIntegrity(const Partition& partition) const @@ -410,9 +406,7 @@ bool VolumeWii::CheckH3TableIntegrity(const Partition& partition) const if (contents.size() != 1) return false; - std::array h3_table_sha1; - mbedtls_sha1_ret(h3_table.data(), h3_table.size(), h3_table_sha1.data()); - return h3_table_sha1 == contents[0].sha1; + return Common::SHA1::CalculateDigest(h3_table) == contents[0].sha1; } bool VolumeWii::CheckBlockIntegrity(u64 block_index, const u8* encrypted_data, @@ -423,7 +417,8 @@ bool VolumeWii::CheckBlockIntegrity(u64 block_index, const u8* encrypted_data, return false; const PartitionDetails& partition_details = it->second; - if (block_index / BLOCKS_PER_GROUP * SHA1_SIZE >= partition_details.h3_table->size()) + if (block_index / BLOCKS_PER_GROUP * Common::SHA1::DIGEST_LEN >= + partition_details.h3_table->size()) return false; mbedtls_aes_context* aes_context = partition_details.key->get(); @@ -438,25 +433,22 @@ bool VolumeWii::CheckBlockIntegrity(u64 block_index, const u8* encrypted_data, for (u32 hash_index = 0; hash_index < 31; ++hash_index) { - u8 h0_hash[SHA1_SIZE]; - mbedtls_sha1_ret(cluster_data + hash_index * 0x400, 0x400, h0_hash); - if (memcmp(h0_hash, hashes.h0[hash_index], SHA1_SIZE)) + if (Common::SHA1::CalculateDigest(cluster_data + hash_index * 0x400, 0x400) != + hashes.h0[hash_index]) return false; } - u8 h1_hash[SHA1_SIZE]; - mbedtls_sha1_ret(reinterpret_cast(hashes.h0), sizeof(hashes.h0), h1_hash); - if (memcmp(h1_hash, hashes.h1[block_index % 8], SHA1_SIZE)) + if (Common::SHA1::CalculateDigest(hashes.h0) != hashes.h1[block_index % 8]) return false; - u8 h2_hash[SHA1_SIZE]; - mbedtls_sha1_ret(reinterpret_cast(hashes.h1), sizeof(hashes.h1), h2_hash); - if (memcmp(h2_hash, hashes.h2[block_index / 8 % 8], SHA1_SIZE)) + if (Common::SHA1::CalculateDigest(hashes.h1) != hashes.h2[block_index / 8 % 8]) return false; - u8 h3_hash[SHA1_SIZE]; - mbedtls_sha1_ret(reinterpret_cast(hashes.h2), sizeof(hashes.h2), h3_hash); - if (memcmp(h3_hash, partition_details.h3_table->data() + block_index / 64 * SHA1_SIZE, SHA1_SIZE)) + Common::SHA1::Digest h3_digest; + auto h3_digest_ptr = + partition_details.h3_table->data() + block_index / 64 * Common::SHA1::DIGEST_LEN; + memcpy(h3_digest.data(), h3_digest_ptr, sizeof(h3_digest)); + if (Common::SHA1::CalculateDigest(hashes.h2) != h3_digest) return false; return true; @@ -496,14 +488,13 @@ bool VolumeWii::HashGroup(const std::array in[BLOCKS_PER_GR { // H0 hashes for (size_t j = 0; j < 31; ++j) - mbedtls_sha1_ret(in[i].data() + j * 0x400, 0x400, out[i].h0[j]); + out[i].h0[j] = Common::SHA1::CalculateDigest(in[i].data() + j * 0x400, 0x400); // H0 padding - std::memset(out[i].padding_0, 0, sizeof(HashBlock::padding_0)); + out[i].padding_0 = {}; // H1 hash - mbedtls_sha1_ret(reinterpret_cast(out[i].h0), sizeof(HashBlock::h0), - out[h1_base].h1[i - h1_base]); + out[h1_base].h1[i - h1_base] = Common::SHA1::CalculateDigest(out[i].h0); } if (i % 8 == 7) @@ -514,15 +505,14 @@ bool VolumeWii::HashGroup(const std::array in[BLOCKS_PER_GR if (success) { // H1 padding - std::memset(out[h1_base].padding_1, 0, sizeof(HashBlock::padding_1)); + out[h1_base].padding_1 = {}; // H1 copies for (size_t j = 1; j < 8; ++j) - std::memcpy(out[h1_base + j].h1, out[h1_base].h1, sizeof(HashBlock::h1)); + out[h1_base + j].h1 = out[h1_base].h1; // H2 hash - mbedtls_sha1_ret(reinterpret_cast(out[i].h1), sizeof(HashBlock::h1), - out[0].h2[h1_base / 8]); + out[0].h2[h1_base / 8] = Common::SHA1::CalculateDigest(out[i].h1); } if (i == BLOCKS_PER_GROUP - 1) @@ -533,11 +523,11 @@ bool VolumeWii::HashGroup(const std::array in[BLOCKS_PER_GR if (success) { // H2 padding - std::memset(out[0].padding_2, 0, sizeof(HashBlock::padding_2)); + out[0].padding_2 = {}; // H2 copies for (size_t j = 1; j < BLOCKS_PER_GROUP; ++j) - std::memcpy(out[j].h2, out[0].h2, sizeof(HashBlock::h2)); + out[j].h2 = out[0].h2; } } } diff --git a/Source/Core/DiscIO/VolumeWii.h b/Source/Core/DiscIO/VolumeWii.h index f2dea1b016..5e0a86a4b9 100644 --- a/Source/Core/DiscIO/VolumeWii.h +++ b/Source/Core/DiscIO/VolumeWii.h @@ -14,6 +14,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Common/Lazy.h" #include "Core/IOS/ES/Formats.h" #include "DiscIO/Filesystem.h" @@ -34,7 +35,6 @@ class VolumeWii : public VolumeDisc { public: static constexpr size_t AES_KEY_SIZE = 16; - static constexpr size_t SHA1_SIZE = 20; static constexpr u32 BLOCKS_PER_GROUP = 0x40; @@ -48,12 +48,12 @@ public: struct HashBlock { - u8 h0[31][SHA1_SIZE]; - u8 padding_0[20]; - u8 h1[8][SHA1_SIZE]; - u8 padding_1[32]; - u8 h2[8][SHA1_SIZE]; - u8 padding_2[32]; + std::array h0; + std::array padding_0; + std::array h1; + std::array padding_1; + std::array h2; + std::array padding_2; }; static_assert(sizeof(HashBlock) == BLOCK_HEADER_SIZE); diff --git a/Source/Core/DiscIO/WIABlob.cpp b/Source/Core/DiscIO/WIABlob.cpp index d11374e507..1ee9dd7308 100644 --- a/Source/Core/DiscIO/WIABlob.cpp +++ b/Source/Core/DiscIO/WIABlob.cpp @@ -15,12 +15,12 @@ #include #include -#include #include #include "Common/Align.h" #include "Common/Assert.h" #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Common/FileUtil.h" #include "Common/IOFile.h" #include "Common/Logging/Log.h" @@ -109,9 +109,8 @@ bool WIARVZFileReader::Initialize(const std::string& path) return false; } - SHA1 header_1_actual_hash; - mbedtls_sha1_ret(reinterpret_cast(&m_header_1), sizeof(m_header_1) - sizeof(SHA1), - header_1_actual_hash.data()); + const auto header_1_actual_hash = Common::SHA1::CalculateDigest( + reinterpret_cast(&m_header_1), sizeof(m_header_1) - Common::SHA1::DIGEST_LEN); if (m_header_1.header_1_hash != header_1_actual_hash) return false; @@ -130,8 +129,7 @@ bool WIARVZFileReader::Initialize(const std::string& path) if (!m_file.ReadBytes(header_2.data(), header_2.size())) return false; - SHA1 header_2_actual_hash; - mbedtls_sha1_ret(header_2.data(), header_2.size(), header_2_actual_hash.data()); + const auto header_2_actual_hash = Common::SHA1::CalculateDigest(header_2); if (m_header_1.header_2_hash != header_2_actual_hash) return false; @@ -168,9 +166,7 @@ bool WIARVZFileReader::Initialize(const std::string& path) if (!m_file.ReadBytes(partition_entries.data(), partition_entries.size())) return false; - SHA1 partition_entries_actual_hash; - mbedtls_sha1_ret(reinterpret_cast(partition_entries.data()), partition_entries.size(), - partition_entries_actual_hash.data()); + const auto partition_entries_actual_hash = Common::SHA1::CalculateDigest(partition_entries); if (m_header_2.partition_entries_hash != partition_entries_actual_hash) return false; @@ -635,8 +631,8 @@ WIARVZFileReader::Chunk::Chunk(File::IOFile* file, u64 offset_in_file, u64 m_rvz_packed_size(rvz_packed_size), m_data_offset(data_offset) { constexpr size_t MAX_SIZE_PER_EXCEPTION_LIST = - Common::AlignUp(VolumeWii::BLOCK_HEADER_SIZE, sizeof(SHA1)) / sizeof(SHA1) * - VolumeWii::BLOCKS_PER_GROUP * sizeof(HashExceptionEntry) + + Common::AlignUp(VolumeWii::BLOCK_HEADER_SIZE, Common::SHA1::DIGEST_LEN) / + Common::SHA1::DIGEST_LEN * VolumeWii::BLOCKS_PER_GROUP * sizeof(HashExceptionEntry) + sizeof(u16); m_out_bytes_allocated_for_exceptions = @@ -861,11 +857,11 @@ bool WIARVZFileReader::ApplyHashExceptions( return false; const size_t offset_in_block = offset % VolumeWii::BLOCK_HEADER_SIZE; - if (offset_in_block + sizeof(SHA1) > VolumeWii::BLOCK_HEADER_SIZE) + if (offset_in_block + Common::SHA1::DIGEST_LEN > VolumeWii::BLOCK_HEADER_SIZE) return false; std::memcpy(reinterpret_cast(&hash_blocks[block_index]) + offset_in_block, &exception.hash, - sizeof(SHA1)); + Common::SHA1::DIGEST_LEN); } return true; @@ -1420,7 +1416,7 @@ WIARVZFileReader::ProcessAndCompress(CompressThreadState* state, CompressPa &aes_context); const auto compare_hash = [&](size_t offset_in_block) { - ASSERT(offset_in_block + sizeof(SHA1) <= VolumeWii::BLOCK_HEADER_SIZE); + ASSERT(offset_in_block + Common::SHA1::DIGEST_LEN <= VolumeWii::BLOCK_HEADER_SIZE); const u8* desired_hash = reinterpret_cast(&hashes) + offset_in_block; const u8* computed_hash = @@ -1432,22 +1428,22 @@ WIARVZFileReader::ProcessAndCompress(CompressThreadState* state, CompressPa // that affects the recalculated hashes. Chunks which have been marked as reusable at // this point normally have zero matching hashes anyway, so this shouldn't waste space. if ((chunks_per_wii_group != 1 && output_entries[chunk_index].reuse_id) || - !std::equal(desired_hash, desired_hash + sizeof(SHA1), computed_hash)) + !std::equal(desired_hash, desired_hash + Common::SHA1::DIGEST_LEN, computed_hash)) { const u64 hash_offset = hash_offset_of_block + offset_in_block; ASSERT(hash_offset <= std::numeric_limits::max()); HashExceptionEntry& exception = exception_lists[exception_list_index].emplace_back(); exception.offset = static_cast(Common::swap16(hash_offset)); - std::memcpy(exception.hash.data(), desired_hash, sizeof(SHA1)); + std::memcpy(exception.hash.data(), desired_hash, Common::SHA1::DIGEST_LEN); } }; const auto compare_hashes = [&compare_hash](size_t offset, size_t size) { - for (size_t l = 0; l < size; l += sizeof(SHA1)) + for (size_t l = 0; l < size; l += Common::SHA1::DIGEST_LEN) // The std::min is to ensure that we don't go beyond the end of HashBlock with - // padding_2, which is 32 bytes long (not divisible by sizeof(SHA1), which is 20). - compare_hash(offset + std::min(l, size - sizeof(SHA1))); + // padding_2, which is 32 bytes long (not divisible by SHA1::DIGEST_LEN, which is 20). + compare_hash(offset + std::min(l, size - Common::SHA1::DIGEST_LEN)); }; using HashBlock = VolumeWii::HashBlock; @@ -1995,10 +1991,7 @@ WIARVZFileReader::Convert(BlobReader* infile, const VolumeDisc* infile_volu header_2.partition_entry_size = Common::swap32(sizeof(PartitionEntry)); header_2.partition_entries_offset = Common::swap64(partition_entries_offset); - if (partition_entries.data() == nullptr) - partition_entries.reserve(1); // Avoid a crash in mbedtls_sha1_ret - mbedtls_sha1_ret(reinterpret_cast(partition_entries.data()), partition_entries_size, - header_2.partition_entries_hash.data()); + header_2.partition_entries_hash = Common::SHA1::CalculateDigest(partition_entries); header_2.number_of_raw_data_entries = Common::swap32(static_cast(raw_data_entries.size())); header_2.raw_data_entries_offset = Common::swap64(raw_data_entries_offset); @@ -2014,12 +2007,12 @@ WIARVZFileReader::Convert(BlobReader* infile, const VolumeDisc* infile_volu header_1.version_compatible = Common::swap32(RVZ ? RVZ_VERSION_WRITE_COMPATIBLE : WIA_VERSION_WRITE_COMPATIBLE); header_1.header_2_size = Common::swap32(sizeof(WIAHeader2)); - mbedtls_sha1_ret(reinterpret_cast(&header_2), sizeof(header_2), - header_1.header_2_hash.data()); + header_1.header_2_hash = + Common::SHA1::CalculateDigest(reinterpret_cast(&header_2), sizeof(header_2)); header_1.iso_file_size = Common::swap64(infile->GetDataSize()); header_1.wia_file_size = Common::swap64(outfile->GetSize()); - mbedtls_sha1_ret(reinterpret_cast(&header_1), offsetof(WIAHeader1, header_1_hash), - header_1.header_1_hash.data()); + header_1.header_1_hash = Common::SHA1::CalculateDigest(reinterpret_cast(&header_1), + offsetof(WIAHeader1, header_1_hash)); if (!outfile->Seek(0, File::SeekOrigin::Begin)) return ConversionResultCode::WriteFailed; diff --git a/Source/Core/DiscIO/WIABlob.h b/Source/Core/DiscIO/WIABlob.h index abfa829397..d998005d06 100644 --- a/Source/Core/DiscIO/WIABlob.h +++ b/Source/Core/DiscIO/WIABlob.h @@ -12,6 +12,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Common/IOFile.h" #include "Common/Swap.h" #include "DiscIO/Blob.h" @@ -70,7 +71,6 @@ public: int compression_level, int chunk_size, CompressCB callback); private: - using SHA1 = std::array; using WiiKey = std::array; // See docs/WiaAndRvz.md for details about the format @@ -82,10 +82,10 @@ private: u32 version; u32 version_compatible; u32 header_2_size; - SHA1 header_2_hash; + Common::SHA1::Digest header_2_hash; u64 iso_file_size; u64 wia_file_size; - SHA1 header_1_hash; + Common::SHA1::Digest header_1_hash; }; static_assert(sizeof(WIAHeader1) == 0x48, "Wrong size for WIA header 1"); @@ -101,7 +101,7 @@ private: u32 number_of_partition_entries; u32 partition_entry_size; u64 partition_entries_offset; - SHA1 partition_entries_hash; + Common::SHA1::Digest partition_entries_hash; u32 number_of_raw_data_entries; u64 raw_data_entries_offset; @@ -161,7 +161,7 @@ private: struct HashExceptionEntry { u16 offset; - SHA1 hash; + Common::SHA1::Digest hash; }; static_assert(sizeof(HashExceptionEntry) == 0x16, "Wrong size for WIA hash exception entry"); #pragma pack(pop) diff --git a/Source/Core/DiscIO/WIACompression.cpp b/Source/Core/DiscIO/WIACompression.cpp index 1bfc3a9149..cb8fdee389 100644 --- a/Source/Core/DiscIO/WIACompression.cpp +++ b/Source/Core/DiscIO/WIACompression.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include "Common/Assert.h" @@ -48,7 +47,6 @@ bool NoneDecompressor::Decompress(const DecompressionBuffer& in, DecompressionBu PurgeDecompressor::PurgeDecompressor(u64 decompressed_size) : m_decompressed_size(decompressed_size) { - mbedtls_sha1_init(&m_sha1_context); } bool PurgeDecompressor::Decompress(const DecompressionBuffer& in, DecompressionBuffer* out, @@ -56,10 +54,10 @@ bool PurgeDecompressor::Decompress(const DecompressionBuffer& in, DecompressionB { if (!m_started) { - mbedtls_sha1_starts_ret(&m_sha1_context); + m_sha1_context = Common::SHA1::CreateContext(); // Include the exception lists in the SHA-1 calculation (but not in the compression...) - mbedtls_sha1_update_ret(&m_sha1_context, in.data.data(), *in_bytes_read); + m_sha1_context->Update(in.data.data(), *in_bytes_read); m_started = true; } @@ -67,7 +65,7 @@ bool PurgeDecompressor::Decompress(const DecompressionBuffer& in, DecompressionB while (!m_done && in.bytes_written != *in_bytes_read && (m_segment_bytes_written < sizeof(m_segment) || out->data.size() != out->bytes_written)) { - if (m_segment_bytes_written == 0 && *in_bytes_read == in.data.size() - sizeof(SHA1)) + if (m_segment_bytes_written == 0 && *in_bytes_read == in.data.size() - Common::SHA1::DIGEST_LEN) { const size_t zeroes_to_write = std::min(m_decompressed_size - m_out_bytes_written, out->data.size() - out->bytes_written); @@ -79,10 +77,9 @@ bool PurgeDecompressor::Decompress(const DecompressionBuffer& in, DecompressionB if (m_out_bytes_written == m_decompressed_size && in.bytes_written == in.data.size()) { - SHA1 actual_hash; - mbedtls_sha1_finish_ret(&m_sha1_context, actual_hash.data()); + const auto actual_hash = m_sha1_context->Finish(); - SHA1 expected_hash; + Common::SHA1::Digest expected_hash; std::memcpy(expected_hash.data(), in.data.data() + *in_bytes_read, expected_hash.size()); *in_bytes_read += expected_hash.size(); @@ -102,7 +99,7 @@ bool PurgeDecompressor::Decompress(const DecompressionBuffer& in, DecompressionB std::memcpy(reinterpret_cast(&m_segment) + m_segment_bytes_written, in.data.data() + *in_bytes_read, bytes_to_copy); - mbedtls_sha1_update_ret(&m_sha1_context, in.data.data() + *in_bytes_read, bytes_to_copy); + m_sha1_context->Update(in.data.data() + *in_bytes_read, bytes_to_copy); *in_bytes_read += bytes_to_copy; m_bytes_read += bytes_to_copy; @@ -134,7 +131,7 @@ bool PurgeDecompressor::Decompress(const DecompressionBuffer& in, DecompressionB std::memcpy(out->data.data() + out->bytes_written, in.data.data() + *in_bytes_read, bytes_to_copy); - mbedtls_sha1_update_ret(&m_sha1_context, in.data.data() + *in_bytes_read, bytes_to_copy); + m_sha1_context->Update(in.data.data() + *in_bytes_read, bytes_to_copy); *in_bytes_read += bytes_to_copy; m_bytes_read += bytes_to_copy; @@ -435,10 +432,7 @@ bool RVZPackDecompressor::Done() const Compressor::~Compressor() = default; -PurgeCompressor::PurgeCompressor() -{ - mbedtls_sha1_init(&m_sha1_context); -} +PurgeCompressor::PurgeCompressor() = default; PurgeCompressor::~PurgeCompressor() = default; @@ -447,14 +441,14 @@ bool PurgeCompressor::Start(std::optional size) m_buffer.clear(); m_bytes_written = 0; - mbedtls_sha1_starts_ret(&m_sha1_context); + m_sha1_context = Common::SHA1::CreateContext(); return true; } bool PurgeCompressor::AddPrecedingDataOnlyForPurgeHashing(const u8* data, size_t size) { - mbedtls_sha1_update_ret(&m_sha1_context, data, size); + m_sha1_context->Update(data, size); return true; } @@ -465,7 +459,7 @@ bool PurgeCompressor::Compress(const u8* data, size_t size) ASSERT_MSG(DISCIO, m_bytes_written == 0, "Calling PurgeCompressor::Compress() twice is not supported"); - m_buffer.resize(size + sizeof(PurgeSegment) + sizeof(SHA1)); + m_buffer.resize(size + sizeof(PurgeSegment) + Common::SHA1::DIGEST_LEN); size_t bytes_read = 0; @@ -517,10 +511,12 @@ bool PurgeCompressor::Compress(const u8* data, size_t size) bool PurgeCompressor::End() { - mbedtls_sha1_update_ret(&m_sha1_context, m_buffer.data(), m_bytes_written); + m_sha1_context->Update(m_buffer.data(), m_bytes_written); - mbedtls_sha1_finish_ret(&m_sha1_context, m_buffer.data() + m_bytes_written); - m_bytes_written += sizeof(SHA1); + const auto digest = m_sha1_context->Finish(); + std::memcpy(m_buffer.data() + m_bytes_written, digest.data(), sizeof(digest)); + + m_bytes_written += sizeof(digest); ASSERT(m_bytes_written <= m_buffer.size()); diff --git a/Source/Core/DiscIO/WIACompression.h b/Source/Core/DiscIO/WIACompression.h index fdabedbed0..d3d5cb34d7 100644 --- a/Source/Core/DiscIO/WIACompression.h +++ b/Source/Core/DiscIO/WIACompression.h @@ -10,10 +10,10 @@ #include #include -#include #include #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "DiscIO/LaggedFibonacciGenerator.h" namespace DiscIO @@ -24,8 +24,6 @@ struct DecompressionBuffer size_t bytes_written = 0; }; -using SHA1 = std::array; - struct PurgeSegment { u32 offset; @@ -71,7 +69,7 @@ private: size_t m_out_bytes_written = 0; bool m_started = false; - mbedtls_sha1_context m_sha1_context; + std::unique_ptr m_sha1_context; }; class Bzip2Decompressor final : public Decompressor @@ -179,7 +177,7 @@ public: private: std::vector m_buffer; size_t m_bytes_written = 0; - mbedtls_sha1_context m_sha1_context; + std::unique_ptr m_sha1_context; }; class Bzip2Compressor final : public Compressor diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 562a357d48..0c73e3d980 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -36,6 +36,7 @@ + @@ -722,6 +723,7 @@ + diff --git a/Source/Core/UICommon/GameFile.cpp b/Source/Core/UICommon/GameFile.cpp index 96628af087..6e33f480e0 100644 --- a/Source/Core/UICommon/GameFile.cpp +++ b/Source/Core/UICommon/GameFile.cpp @@ -18,13 +18,13 @@ #include #include -#include #include #include "Common/BitUtils.h" #include "Common/ChunkFile.h" #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" +#include "Common/Crypto/SHA1.h" #include "Common/FileUtil.h" #include "Common/HttpRequest.h" #include "Common/IOFile.h" @@ -627,22 +627,18 @@ std::string GameFile::GetNetPlayName(const Core::TitleDatabase& title_database) return name + " (" + ss.str() + ")"; } -static std::array GetHash(u32 value) +static Common::SHA1::Digest GetHash(u32 value) { auto data = Common::BitCastToArray(value); - std::array hash; - mbedtls_sha1_ret(reinterpret_cast(data.data()), data.size(), hash.data()); - return hash; + return Common::SHA1::CalculateDigest(data); } -static std::array GetHash(std::string_view str) +static Common::SHA1::Digest GetHash(std::string_view str) { - std::array hash; - mbedtls_sha1_ret(reinterpret_cast(str.data()), str.size(), hash.data()); - return hash; + return Common::SHA1::CalculateDigest(str); } -static std::optional> GetFileHash(const std::string& path) +static std::optional GetFileHash(const std::string& path) { std::string buffer; if (!File::ReadFileToString(path, buffer)) @@ -650,22 +646,22 @@ static std::optional> GetFileHash(const std::string& path) return GetHash(buffer); } -static std::optional> MixHash(const std::optional>& lhs, - const std::optional>& rhs) +static std::optional MixHash(const std::optional& lhs, + const std::optional& rhs) { if (!lhs && !rhs) return std::nullopt; if (!lhs || !rhs) return !rhs ? lhs : rhs; - std::array result; + Common::SHA1::Digest result; for (size_t i = 0; i < result.size(); ++i) result[i] = (*lhs)[i] ^ (*rhs)[(i + 1) % result.size()]; return result; } -std::array GameFile::GetSyncHash() const +Common::SHA1::Digest GameFile::GetSyncHash() const { - std::optional> hash; + std::optional hash; if (m_platform == DiscIO::Platform::ELFOrDOL) { @@ -703,7 +699,7 @@ std::array GameFile::GetSyncHash() const hash = volume->GetSyncHash(); } - return hash.value_or(std::array{}); + return hash.value_or(Common::SHA1::Digest{}); } NetPlay::SyncIdentifier GameFile::GetSyncIdentifier() const diff --git a/Source/UnitTests/Common/CMakeLists.txt b/Source/UnitTests/Common/CMakeLists.txt index 2d30517614..bbea892fdf 100644 --- a/Source/UnitTests/Common/CMakeLists.txt +++ b/Source/UnitTests/Common/CMakeLists.txt @@ -5,6 +5,7 @@ add_dolphin_test(BlockingLoopTest BlockingLoopTest.cpp) add_dolphin_test(BusyLoopTest BusyLoopTest.cpp) add_dolphin_test(CommonFuncsTest CommonFuncsTest.cpp) add_dolphin_test(CryptoEcTest Crypto/EcTest.cpp) +add_dolphin_test(CryptoSHA1Test Crypto/SHA1Test.cpp) add_dolphin_test(EnumFormatterTest EnumFormatterTest.cpp) add_dolphin_test(EventTest EventTest.cpp) add_dolphin_test(FileUtilTest FileUtilTest.cpp) diff --git a/Source/UnitTests/Common/Crypto/SHA1Test.cpp b/Source/UnitTests/Common/Crypto/SHA1Test.cpp new file mode 100644 index 0000000000..463240ed84 --- /dev/null +++ b/Source/UnitTests/Common/Crypto/SHA1Test.cpp @@ -0,0 +1,30 @@ +#include + +#include "Common/Crypto/SHA1.h" + +// Just a few quick sanity checks +TEST(SHA1, Vectors) +{ + struct + { + const char* msg; + const Common::SHA1::Digest expected; + } const vectors[]{ + {"", {0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, + 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09}}, + {"abc", {0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e, + 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d}}, + {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + {0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae, + 0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1}}, + {"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmn" + "opqklmnopqrlmnopqrsmnopqrstnopqrstu", + {0xa4, 0x9b, 0x24, 0x46, 0xa0, 0x2c, 0x64, 0x5b, 0xf4, 0x19, + 0xf9, 0x95, 0xb6, 0x70, 0x91, 0x25, 0x3a, 0x04, 0xa2, 0x59}}, + }; + for (auto& test : vectors) + { + auto actual = Common::SHA1::CalculateDigest(test.msg); + EXPECT_EQ(test.expected, actual); + } +} diff --git a/Source/UnitTests/UnitTests.vcxproj b/Source/UnitTests/UnitTests.vcxproj index da461087e9..e0295fba9c 100644 --- a/Source/UnitTests/UnitTests.vcxproj +++ b/Source/UnitTests/UnitTests.vcxproj @@ -44,6 +44,7 @@ +