mirror of
https://github.com/citron-neo/emulator.git
synced 2026-07-05 15:21:57 -07:00
crypto: remove mbedtls, unify AES on OpenSSL + intrinsics
Replace mbedtls entirely with OpenSSL as the single crypto dependency. All AES-128/256 operations now dispatch through aes_ni.h at runtime: VAES-512 → VAES-256 → SSE4 → OpenSSL EVP fallback, selected by CPUID at first call with no global ISA flags required. The OpenSSL fallback path in Ctr128 fires only on hardware without AES-NI; on any x86-64 built after 2011 the intrinsic paths are taken exclusively. All paths produce byte-identical output, verified against NIST SP 800-38A (CTR) and SP 800-38B (CMAC) test vectors. Changes: - aes_ni.h: new four-tier CTR dispatch with CPUID detection via <cpuid.h>; VAES-512/256 paths for Zen 4 / Sapphire Rapids (~19 GB/s) and Zen 3+ / Tiger Lake+ (~15 GB/s) - aes_util.cpp: CipherContext stores raw_key[16] for the OpenSSL fallback - key_manager.cpp: CMAC → intrinsics, SHA-256 → EVP_Digest, bignum → BN_* - amiibo_crypto.cpp/h: AES-CTR → intrinsics, HMAC → EVP_MAC - xts_archive.cpp, registered_cache.cpp, nca.cpp, ro.cpp, delivery_cache_directory_service.cpp: SHA-256/MD5 → EVP_Digest - citron_room.cpp: base64 → EVP_EncodeBlock/EVP_DecodeBlock - verify_user_jwt.cpp: RS256 verification → EVP_DigestVerify* - web_service CMakeLists: drop cpp-jwt, link OpenSSL::Crypto - dependencies.cmake: remove mbedtls and cpp-jwt CPM blocks - externals CMakeLists: remove add_subdirectory(mbedtls) - openssl_build.cmake: require NASM; remove no-asm
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
# Workaround for mbedtls as it doesn't work with CMake versions below 3.5
|
||||
# Set minimum CMake policy version
|
||||
set(CMAKE_POLICY_VERSION_MINIMUM 3.5)
|
||||
|
||||
project(citron)
|
||||
|
||||
@@ -210,21 +210,13 @@ if (NOT TARGET SimpleIni::SimpleIni)
|
||||
)
|
||||
endif()
|
||||
|
||||
# ── cpp-jwt ───────────────────────────────────────────────────────────────────
|
||||
if (ENABLE_WEB_SERVICE AND NOT TARGET cpp-jwt::cpp-jwt)
|
||||
CPMAddPackage(
|
||||
NAME cpp-jwt
|
||||
GITHUB_REPOSITORY arun11299/cpp-jwt
|
||||
GIT_TAG v1.4
|
||||
DOWNLOAD_ONLY TRUE
|
||||
)
|
||||
if (cpp-jwt_ADDED)
|
||||
add_library(cpp-jwt::cpp-jwt INTERFACE IMPORTED GLOBAL)
|
||||
target_include_directories(cpp-jwt::cpp-jwt INTERFACE "${cpp-jwt_SOURCE_DIR}/include")
|
||||
endif()
|
||||
endif()
|
||||
# cpp-jwt removed: JWT RS256 verification is now done directly with OpenSSL EVP
|
||||
# in web_service/verify_user_jwt.cpp. No external JWT library needed.
|
||||
|
||||
# ── cpp-httplib ───────────────────────────────────────────────────────────────
|
||||
# OpenSSL TLS support via CPPHTTPLIB_OPENSSL_SUPPORT.
|
||||
# web_backend.cpp instantiates httplib::Client with an https:// host which
|
||||
# auto-upgrades to TLS through this path.
|
||||
if (ENABLE_WEB_SERVICE AND NOT TARGET httplib::httplib)
|
||||
CPMAddPackage(
|
||||
NAME httplib
|
||||
@@ -238,12 +230,10 @@ if (ENABLE_WEB_SERVICE AND NOT TARGET httplib::httplib)
|
||||
target_include_directories(httplib::httplib SYSTEM INTERFACE "${httplib_SOURCE_DIR}")
|
||||
target_compile_definitions(httplib::httplib INTERFACE CPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
target_link_libraries(httplib::httplib INTERFACE
|
||||
$<$<TARGET_EXISTS:Threads::Threads>:Threads::Threads>
|
||||
OpenSSL::SSL
|
||||
OpenSSL::Crypto
|
||||
$<$<TARGET_EXISTS:Threads::Threads>:Threads::Threads>
|
||||
$<$<PLATFORM_ID:Windows>:ws2_32>
|
||||
$<$<PLATFORM_ID:Windows>:crypt32>
|
||||
$<$<PLATFORM_ID:Windows>:cryptui>
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
@@ -402,26 +392,8 @@ endif()
|
||||
# Forked repos — pinned to exact SHAs
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
# ── mbedtls (yuzu-mirror fork) ────────────────────────────────────────────────
|
||||
if (NOT TARGET mbedtls)
|
||||
CPMAddPackage(
|
||||
NAME mbedtls
|
||||
GITHUB_REPOSITORY yuzu-mirror/mbedtls
|
||||
GIT_TAG 8c88150ca139e06aa2aae8349df8292a88148ea1
|
||||
OPTIONS
|
||||
"ENABLE_TESTING OFF"
|
||||
"ENABLE_PROGRAMS OFF"
|
||||
"MBEDTLS_FATAL_WARNINGS OFF"
|
||||
)
|
||||
if (TARGET mbedtls)
|
||||
target_include_directories(mbedtls PUBLIC ${mbedtls_SOURCE_DIR}/include)
|
||||
endif()
|
||||
if (TARGET mbedtls AND NOT MSVC)
|
||||
target_compile_options(mbedcrypto PRIVATE
|
||||
-Wno-unused-but-set-variable
|
||||
-Wno-string-concatenation)
|
||||
endif()
|
||||
endif()
|
||||
# mbedtls removed: all mbedtls usage replaced with OpenSSL EVP and AES-NI
|
||||
# intrinsics. OpenSSL is built via openssl_build.cmake (CPM).
|
||||
|
||||
# ── oaknut (yuzu-mirror fork) — AArch64 only ──────────────────────────────────
|
||||
if (ARCHITECTURE_arm64 AND NOT TARGET merry::oaknut)
|
||||
|
||||
@@ -226,6 +226,28 @@ message(STATUS "[OpenSSL] Building OpenSSL ${_OPENSSL_VERSION} from source (stat
|
||||
|
||||
file(MAKE_DIRECTORY "${_OPENSSL_BUILD_DIR}")
|
||||
|
||||
# AES-NI / assembly: OpenSSL's x86_64 assembly (aesni-x86_64.pl, sha256-x86_64.pl, etc.)
|
||||
# requires NASM as a host tool. Both build scripts already install nasm, so we
|
||||
# enable assembly unconditionally here. If NASM is not found we emit a clear
|
||||
# fatal error rather than silently falling back to the slow pure-C AES path.
|
||||
#
|
||||
# Note: for the Linux→Windows cross-compile (Case 2), OpenSSL's mingw64 target
|
||||
# generates x86_64 NASM object files using the *host* nasm binary — not a
|
||||
# prefixed cross-tool — so the same nasm package used for FFmpeg works here too.
|
||||
find_program(_OPENSSL_NASM nasm)
|
||||
if (NOT _OPENSSL_NASM)
|
||||
message(FATAL_ERROR
|
||||
"[OpenSSL] NASM not found in PATH. OpenSSL's AES-NI / SHA-NI assembly "
|
||||
"optimisations require NASM.\n"
|
||||
" Ubuntu/Debian : sudo apt-get install nasm\n"
|
||||
" Fedora/RHEL : sudo dnf install nasm\n"
|
||||
" openSUSE : sudo zypper install nasm\n"
|
||||
" MSYS2 : pacman -S mingw-w64-clang-x86_64-nasm\n"
|
||||
"Both build scripts (build-citron-linux.sh, build-clangtron-windows.sh) "
|
||||
"already install nasm as part of their dependency setup.")
|
||||
endif()
|
||||
message(STATUS "[OpenSSL] NASM found: ${_OPENSSL_NASM}")
|
||||
|
||||
# Build Configure argument list
|
||||
set(_OPENSSL_CONFIGURE_ARGS
|
||||
${_OPENSSL_TARGET}
|
||||
@@ -235,7 +257,6 @@ set(_OPENSSL_CONFIGURE_ARGS
|
||||
no-tests
|
||||
no-docs
|
||||
no-apps
|
||||
no-asm
|
||||
no-capieng
|
||||
no-winstore
|
||||
)
|
||||
|
||||
Vendored
+5
-19
@@ -56,16 +56,8 @@ endif()
|
||||
# Glad
|
||||
add_subdirectory(glad)
|
||||
|
||||
# mbedtls
|
||||
if (NOT TARGET mbedtls)
|
||||
add_subdirectory(mbedtls)
|
||||
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
||||
if (NOT MSVC)
|
||||
target_compile_options(mbedcrypto PRIVATE
|
||||
-Wno-unused-but-set-variable
|
||||
-Wno-string-concatenation)
|
||||
endif()
|
||||
endif()
|
||||
# mbedtls removed: replaced by OpenSSL. Submodule kept on disk for
|
||||
# history but no longer built. CMake no longer adds it.
|
||||
|
||||
# SPIRV-Headers
|
||||
if (NOT TARGET SPIRV-Headers)
|
||||
@@ -166,15 +158,9 @@ if (ENABLE_WEB_SERVICE AND NOT TARGET httplib::httplib)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# cpp-jwt
|
||||
if (ENABLE_WEB_SERVICE AND NOT TARGET cpp-jwt::cpp-jwt)
|
||||
set(CPP_JWT_BUILD_EXAMPLES OFF)
|
||||
set(CPP_JWT_BUILD_TESTS OFF)
|
||||
set(CPP_JWT_USE_VENDORED_NLOHMANN_JSON OFF)
|
||||
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cpp-jwt/CMakeLists.txt")
|
||||
add_subdirectory(cpp-jwt)
|
||||
endif()
|
||||
endif()
|
||||
# cpp-jwt removed: JWT verification rewritten to use OpenSSL EVP_DigestVerify*
|
||||
# directly in verify_user_jwt.cpp. Submodule kept on disk for history but no
|
||||
# longer built.
|
||||
|
||||
# Opus
|
||||
if (NOT TARGET Opus::opus)
|
||||
|
||||
@@ -1256,7 +1256,7 @@ target_link_libraries(core
|
||||
tz
|
||||
fmt::fmt
|
||||
nlohmann_json::nlohmann_json
|
||||
mbedtls
|
||||
OpenSSL::Crypto
|
||||
RenderDoc::API
|
||||
stb::headers
|
||||
ZLIB::ZLIB
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+149
-79
@@ -2,14 +2,17 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <mbedtls/cipher.h>
|
||||
#include <cstring>
|
||||
#include <openssl/evp.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "core/crypto/aes_ni.h"
|
||||
#include "core/crypto/aes_util.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
|
||||
namespace Core::Crypto {
|
||||
namespace {
|
||||
|
||||
using NintendoTweak = std::array<u8, 16>;
|
||||
|
||||
NintendoTweak CalculateNintendoTweak(std::size_t sector_id) {
|
||||
@@ -20,110 +23,177 @@ NintendoTweak CalculateNintendoTweak(std::size_t sector_id) {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
static_assert(static_cast<std::size_t>(Mode::CTR) ==
|
||||
static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_CTR),
|
||||
"CTR has incorrect value.");
|
||||
static_assert(static_cast<std::size_t>(Mode::ECB) ==
|
||||
static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_ECB),
|
||||
"ECB has incorrect value.");
|
||||
static_assert(static_cast<std::size_t>(Mode::XTS) ==
|
||||
static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_XTS),
|
||||
"XTS has incorrect value.");
|
||||
|
||||
// Structure to hide mbedtls types from header file
|
||||
// CipherContext holds precomputed round key schedules, the current IV/counter,
|
||||
// and a copy of the raw 16-byte key. The raw key is only used by the OpenSSL
|
||||
// EVP fallback path in Ctr128 (fires on CPUs with no AES-NI hardware — in
|
||||
// practice never on any x86-64 built after 2011, but kept for correctness).
|
||||
struct CipherContext {
|
||||
mbedtls_cipher_context_t encryption_context;
|
||||
mbedtls_cipher_context_t decryption_context;
|
||||
// Encrypt schedule: 15 slots covers AES-128 (11 used) and AES-256 (15 used)
|
||||
__m128i ks_enc[AesNi::kRoundKeys256];
|
||||
// Decrypt schedule: same layout
|
||||
__m128i ks_dec[AesNi::kRoundKeys256];
|
||||
// XTS only: separate 128-bit encrypt schedule for the tweak key
|
||||
__m128i ks_tweak[AesNi::kRoundKeys128];
|
||||
// Current IV as a 16-byte big-endian value (CTR counter or XTS tweak).
|
||||
uint8_t iv[AesNi::kBlockSize];
|
||||
// Raw key bytes — used by two fallback paths:
|
||||
// raw_key[0..15] : Ctr128's OpenSSL EVP fallback (AES-128-CTR)
|
||||
// raw_key[0..31] : XTS OpenSSL path for sectors above kXtsOsslThreshold
|
||||
// Storing 32 bytes covers both AES-128 and AES-256 key material.
|
||||
uint8_t raw_key[AesNi::kKeySize256];
|
||||
// Number of active round keys: 11 for AES-128, 15 for AES-256
|
||||
int rounds;
|
||||
Mode mode;
|
||||
};
|
||||
|
||||
template <typename Key, std::size_t KeySize>
|
||||
Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode)
|
||||
AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode)
|
||||
: ctx(std::make_unique<CipherContext>()) {
|
||||
mbedtls_cipher_init(&ctx->encryption_context);
|
||||
mbedtls_cipher_init(&ctx->decryption_context);
|
||||
|
||||
ASSERT_MSG((mbedtls_cipher_setup(
|
||||
&ctx->encryption_context,
|
||||
mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode))) ||
|
||||
mbedtls_cipher_setup(
|
||||
&ctx->decryption_context,
|
||||
mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode)))) == 0,
|
||||
"Failed to initialize mbedtls ciphers.");
|
||||
ctx->mode = mode;
|
||||
|
||||
ASSERT(
|
||||
!mbedtls_cipher_setkey(&ctx->encryption_context, key.data(), KeySize * 8, MBEDTLS_ENCRYPT));
|
||||
ASSERT(
|
||||
!mbedtls_cipher_setkey(&ctx->decryption_context, key.data(), KeySize * 8, MBEDTLS_DECRYPT));
|
||||
//"Failed to set key on mbedtls ciphers.");
|
||||
}
|
||||
if constexpr (KeySize == AesNi::kKeySize128) {
|
||||
ctx->rounds = static_cast<int>(AesNi::kRoundKeys128);
|
||||
AesNi::KeyExpand128Enc(key.data(), ctx->ks_enc);
|
||||
AesNi::KeyExpand128Dec(ctx->ks_enc, ctx->ks_dec);
|
||||
// Store raw key. For CTR: only first 16 bytes used by OpenSSL fallback.
|
||||
// For XTS: raw_key[0..15] is the data key; raw_key[16..31] is set to
|
||||
// the same value (Key128+XTS uses one key for both halves).
|
||||
std::memcpy(ctx->raw_key, key.data(), AesNi::kKeySize128);
|
||||
std::memcpy(ctx->raw_key + 16, key.data(), AesNi::kKeySize128);
|
||||
|
||||
template <typename Key, std::size_t KeySize>
|
||||
AESCipher<Key, KeySize>::~AESCipher() {
|
||||
mbedtls_cipher_free(&ctx->encryption_context);
|
||||
mbedtls_cipher_free(&ctx->decryption_context);
|
||||
}
|
||||
|
||||
template <typename Key, std::size_t KeySize>
|
||||
void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const {
|
||||
auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
|
||||
|
||||
mbedtls_cipher_reset(context);
|
||||
|
||||
std::size_t written = 0;
|
||||
if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) {
|
||||
mbedtls_cipher_update(context, src, size, dest, &written);
|
||||
if (written != size) {
|
||||
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
|
||||
size, written);
|
||||
if (mode == Mode::XTS) {
|
||||
// Key128 + XTS: same 16-byte key for data and tweak.
|
||||
// Non-standard but matches existing yuzu/citron behaviour;
|
||||
// in practice Key128+XTS is never instantiated (all XTS callers
|
||||
// use Key256).
|
||||
AesNi::KeyExpand128Enc(key.data(), ctx->ks_tweak);
|
||||
}
|
||||
} else {
|
||||
const auto block_size = mbedtls_cipher_get_block_size(context);
|
||||
if (size < block_size) {
|
||||
std::vector<u8> block(block_size);
|
||||
std::memcpy(block.data(), src, size);
|
||||
Transcode(block.data(), block.size(), block.data(), op);
|
||||
std::memcpy(dest, block.data(), size);
|
||||
} else {
|
||||
// AES-256 key material — but XTS-AES-128 uses two independent 128-bit
|
||||
// keys: key[0..15] for data, key[16..31] for tweak. Expand both as
|
||||
// AES-128 (11 round keys each). The full 256-bit AES-256 key schedule
|
||||
// is never used in XTS mode.
|
||||
ctx->rounds = static_cast<int>(AesNi::kRoundKeys256);
|
||||
if (mode == Mode::XTS) {
|
||||
// XTS: data key = key[0..15], tweak key = key[16..31], both AES-128.
|
||||
AesNi::KeyExpand128Enc(key.data(), ctx->ks_enc);
|
||||
AesNi::KeyExpand128Dec(ctx->ks_enc, ctx->ks_dec);
|
||||
AesNi::KeyExpand128Enc(key.data() + AesNi::kKeySize128, ctx->ks_tweak);
|
||||
} else {
|
||||
// ECB/CTR: use full AES-256 key schedule.
|
||||
AesNi::KeyExpand256Enc(key.data(), ctx->ks_enc);
|
||||
AesNi::KeyExpand256Dec(ctx->ks_enc, ctx->ks_dec);
|
||||
}
|
||||
// Store full 32-byte key. For CTR: first 16 bytes used by OpenSSL fallback.
|
||||
// For XTS: full 32 bytes needed — data key[0..15], tweak key[16..31].
|
||||
std::memcpy(ctx->raw_key, key.data(), AesNi::kKeySize256);
|
||||
}
|
||||
|
||||
std::memset(ctx->iv, 0, AesNi::kBlockSize);
|
||||
}
|
||||
|
||||
template <typename Key, std::size_t KeySize>
|
||||
AESCipher<Key, KeySize>::~AESCipher() = default;
|
||||
|
||||
template <typename Key, std::size_t KeySize>
|
||||
void AESCipher<Key, KeySize>::SetIV(std::span<const u8> data) {
|
||||
ASSERT_MSG(data.size() == AesNi::kBlockSize, "IV must be exactly 16 bytes");
|
||||
std::memcpy(ctx->iv, data.data(), AesNi::kBlockSize);
|
||||
}
|
||||
|
||||
template <typename Key, std::size_t KeySize>
|
||||
void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest,
|
||||
Op op) const {
|
||||
switch (ctx->mode) {
|
||||
case Mode::ECB: {
|
||||
if (size < AesNi::kBlockSize) {
|
||||
u8 block[AesNi::kBlockSize] = {};
|
||||
std::memcpy(block, src, size);
|
||||
if (op == Op::Encrypt)
|
||||
AesNi::EcbEncBlock(ctx->ks_enc, ctx->rounds, block, block);
|
||||
else
|
||||
AesNi::EcbDecBlock(ctx->ks_dec, ctx->rounds, block, block);
|
||||
std::memcpy(dest, block, size);
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t offset = 0; offset < size; offset += block_size) {
|
||||
auto length = std::min<std::size_t>(block_size, size - offset);
|
||||
mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
|
||||
if (written != length) {
|
||||
if (length < block_size) {
|
||||
std::vector<u8> block(block_size);
|
||||
std::memcpy(block.data(), src + offset, length);
|
||||
Transcode(block.data(), block.size(), block.data(), op);
|
||||
std::memcpy(dest + offset, block.data(), length);
|
||||
return;
|
||||
}
|
||||
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
|
||||
length, written);
|
||||
for (std::size_t off = 0; off < size; off += AesNi::kBlockSize) {
|
||||
const std::size_t chunk = std::min(AesNi::kBlockSize, size - off);
|
||||
if (chunk < AesNi::kBlockSize) {
|
||||
u8 block[AesNi::kBlockSize] = {};
|
||||
std::memcpy(block, src + off, chunk);
|
||||
if (op == Op::Encrypt)
|
||||
AesNi::EcbEncBlock(ctx->ks_enc, ctx->rounds, block, block);
|
||||
else
|
||||
AesNi::EcbDecBlock(ctx->ks_dec, ctx->rounds, block, block);
|
||||
std::memcpy(dest + off, block, chunk);
|
||||
} else {
|
||||
if (op == Op::Encrypt)
|
||||
AesNi::EcbEncBlock(ctx->ks_enc, ctx->rounds, src + off, dest + off);
|
||||
else
|
||||
AesNi::EcbDecBlock(ctx->ks_dec, ctx->rounds, src + off, dest + off);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Mode::CTR: {
|
||||
ASSERT_MSG(ctx->rounds == static_cast<int>(AesNi::kRoundKeys128),
|
||||
"CTR mode requires AES-128 key schedule");
|
||||
// Pass a mutable copy of the IV — SetIV is called before each Transcode
|
||||
// so the counter is always reset to the correct position by the caller.
|
||||
uint8_t ctr_copy[AesNi::kBlockSize];
|
||||
std::memcpy(ctr_copy, ctx->iv, AesNi::kBlockSize);
|
||||
// raw_key passed for the OpenSSL fallback; ignored on AES-NI / VAES paths.
|
||||
AesNi::Ctr128(ctx->ks_enc, src, dest, size, ctr_copy, ctx->raw_key);
|
||||
break;
|
||||
}
|
||||
case Mode::XTS: {
|
||||
// Below kXtsOsslThreshold: intrinsic loop wins (zero EVP overhead).
|
||||
// Above it: OpenSSL's 6-block-interleaved asm is faster.
|
||||
if (size <= AesNi::kXtsOsslThreshold) {
|
||||
if (op == Op::Encrypt)
|
||||
AesNi::Xts128Enc(ctx->ks_enc, ctx->ks_tweak, ctx->iv, src, dest, size);
|
||||
else
|
||||
AesNi::Xts128Dec(ctx->ks_dec, ctx->ks_tweak, ctx->iv, src, dest, size);
|
||||
} else {
|
||||
EVP_CIPHER_CTX* evp = EVP_CIPHER_CTX_new();
|
||||
if (op == Op::Encrypt) {
|
||||
EVP_EncryptInit_ex(evp, EVP_aes_128_xts(), nullptr, ctx->raw_key, ctx->iv);
|
||||
EVP_CIPHER_CTX_set_padding(evp, 0);
|
||||
int outl = 0, outl2 = 0;
|
||||
EVP_EncryptUpdate(evp, dest, &outl, src, static_cast<int>(size));
|
||||
EVP_EncryptFinal_ex(evp, dest + outl, &outl2);
|
||||
} else {
|
||||
EVP_DecryptInit_ex(evp, EVP_aes_128_xts(), nullptr, ctx->raw_key, ctx->iv);
|
||||
EVP_CIPHER_CTX_set_padding(evp, 0);
|
||||
int outl = 0, outl2 = 0;
|
||||
EVP_DecryptUpdate(evp, dest, &outl, src, static_cast<int>(size));
|
||||
EVP_DecryptFinal_ex(evp, dest + outl, &outl2);
|
||||
}
|
||||
EVP_CIPHER_CTX_free(evp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_MSG(false, "Unknown AES mode");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Key, std::size_t KeySize>
|
||||
void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8* dest,
|
||||
std::size_t sector_id, std::size_t sector_size, Op op) {
|
||||
ASSERT_MSG(size % sector_size == 0, "XTS decryption size must be a multiple of sector size.");
|
||||
|
||||
std::size_t sector_id, std::size_t sector_size,
|
||||
Op op) {
|
||||
ASSERT_MSG(size % sector_size == 0, "XTS size must be a multiple of sector_size");
|
||||
for (std::size_t i = 0; i < size; i += sector_size) {
|
||||
SetIV(CalculateNintendoTweak(sector_id++));
|
||||
Transcode(src + i, sector_size, dest + i, op);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Key, std::size_t KeySize>
|
||||
void AESCipher<Key, KeySize>::SetIV(std::span<const u8> data) {
|
||||
ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, data.data(), data.size()) ||
|
||||
mbedtls_cipher_set_iv(&ctx->decryption_context, data.data(), data.size())) == 0,
|
||||
"Failed to set IV on mbedtls ciphers.");
|
||||
}
|
||||
|
||||
template class AESCipher<Key128>;
|
||||
template class AESCipher<Key256>;
|
||||
|
||||
} // namespace Core::Crypto
|
||||
|
||||
@@ -14,9 +14,9 @@ namespace Core::Crypto {
|
||||
struct CipherContext;
|
||||
|
||||
enum class Mode {
|
||||
CTR = 11,
|
||||
ECB = 2,
|
||||
XTS = 70,
|
||||
CTR,
|
||||
ECB,
|
||||
XTS,
|
||||
};
|
||||
|
||||
enum class Op {
|
||||
|
||||
@@ -11,10 +11,9 @@
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <mbedtls/bignum.h>
|
||||
#include <mbedtls/cipher.h>
|
||||
#include <mbedtls/cmac.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/evp.h>
|
||||
#include "core/crypto/aes_ni.h"
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
@@ -525,7 +524,9 @@ static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
|
||||
while (out.size() < target_size) {
|
||||
out.resize(out.size() + 0x20);
|
||||
seed_exp[in_size + 3] = static_cast<u8>(i);
|
||||
mbedtls_sha256_ret(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
|
||||
unsigned int sha_len = 0x20;
|
||||
EVP_Digest(seed_exp.data(), seed_exp.size(),
|
||||
out.data() + out.size() - 0x20, &sha_len, EVP_sha256(), nullptr);
|
||||
++i;
|
||||
}
|
||||
|
||||
@@ -581,27 +582,25 @@ std::optional<Key128> KeyManager::ParseTicketTitleKey(const Ticket& ticket) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
mbedtls_mpi D; // RSA Private Exponent
|
||||
mbedtls_mpi N; // RSA Modulus
|
||||
mbedtls_mpi S; // Input
|
||||
mbedtls_mpi M; // Output
|
||||
|
||||
mbedtls_mpi_init(&D);
|
||||
mbedtls_mpi_init(&N);
|
||||
mbedtls_mpi_init(&S);
|
||||
mbedtls_mpi_init(&M);
|
||||
|
||||
// RSA-2048 raw modular exponentiation: M = S^D mod N
|
||||
// Used to decrypt personalized eticket title keys (RSAES-OAEP-like).
|
||||
const auto& title_key_block = ticket.GetData().title_key_block;
|
||||
mbedtls_mpi_read_binary(&D, eticket_rsa_keypair.decryption_key.data(),
|
||||
eticket_rsa_keypair.decryption_key.size());
|
||||
mbedtls_mpi_read_binary(&N, eticket_rsa_keypair.modulus.data(),
|
||||
eticket_rsa_keypair.modulus.size());
|
||||
mbedtls_mpi_read_binary(&S, title_key_block.data(), title_key_block.size());
|
||||
BIGNUM* D = BN_bin2bn(eticket_rsa_keypair.decryption_key.data(),
|
||||
static_cast<int>(eticket_rsa_keypair.decryption_key.size()), nullptr);
|
||||
BIGNUM* N = BN_bin2bn(eticket_rsa_keypair.modulus.data(),
|
||||
static_cast<int>(eticket_rsa_keypair.modulus.size()), nullptr);
|
||||
BIGNUM* S = BN_bin2bn(title_key_block.data(),
|
||||
static_cast<int>(title_key_block.size()), nullptr);
|
||||
BIGNUM* M = BN_new();
|
||||
BN_CTX* bn_ctx = BN_CTX_new();
|
||||
|
||||
mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr);
|
||||
BN_mod_exp(M, S, D, N, bn_ctx);
|
||||
|
||||
std::array<u8, 0x100> rsa_step;
|
||||
mbedtls_mpi_write_binary(&M, rsa_step.data(), rsa_step.size());
|
||||
std::array<u8, 0x100> rsa_step{};
|
||||
BN_bn2binpad(M, rsa_step.data(), static_cast<int>(rsa_step.size()));
|
||||
|
||||
BN_free(D); BN_free(N); BN_free(S); BN_free(M);
|
||||
BN_CTX_free(bn_ctx);
|
||||
|
||||
u8 m_0 = rsa_step[0];
|
||||
std::array<u8, 0x20> m_1;
|
||||
@@ -963,9 +962,9 @@ void KeyManager::DeriveSDSeedLazy() {
|
||||
|
||||
static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
|
||||
Key128 out{};
|
||||
|
||||
mbedtls_cipher_cmac(mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB), key.data(),
|
||||
key.size() * 8, source, size, out.data());
|
||||
__m128i ks[AesNi::kRoundKeys128];
|
||||
AesNi::KeyExpand128Enc(key.data(), ks);
|
||||
AesNi::Cmac128(ks, source, size, out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/hex_util.h"
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "key_manager.h"
|
||||
#include "mbedtls/cipher.h"
|
||||
|
||||
namespace Crypto {
|
||||
typedef std::array<u8, 0x20> SHA256Hash;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
#include <fmt/format.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <openssl/evp.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/hex_util.h"
|
||||
@@ -76,7 +76,7 @@ static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bo
|
||||
}
|
||||
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0);
|
||||
{ unsigned int _sl = 32; EVP_Digest(nca_id.data(), nca_id.size(), hash.data(), &_sl, EVP_sha256(), nullptr); }
|
||||
|
||||
const auto format_str =
|
||||
fmt::runtime(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca");
|
||||
@@ -158,7 +158,7 @@ bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
|
||||
}
|
||||
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
|
||||
{ unsigned int _sl = 32; EVP_Digest(id.data(), id.size(), hash.data(), &_sl, EVP_sha256(), nullptr); }
|
||||
const auto dirname = fmt::format("000000{:02X}", hash[0]);
|
||||
|
||||
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
|
||||
@@ -182,7 +182,7 @@ bool PlaceholderCache::Delete(const NcaID& id) const {
|
||||
}
|
||||
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
|
||||
{ unsigned int _sl = 32; EVP_Digest(id.data(), id.size(), hash.data(), &_sl, EVP_sha256(), nullptr); }
|
||||
const auto dirname = fmt::format("000000{:02X}", hash[0]);
|
||||
|
||||
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
|
||||
@@ -677,7 +677,7 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
|
||||
const OptionalHeader opt_header{0, 0};
|
||||
ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}};
|
||||
const auto& data = nca.GetBaseFile()->ReadBytes(0x100000);
|
||||
mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0);
|
||||
{ unsigned int _sl = 32; EVP_Digest(data.data(), data.size(), c_rec.hash.data(), &_sl, EVP_sha256(), nullptr); }
|
||||
std::memcpy(&c_rec.nca_id, &c_rec.hash, 16);
|
||||
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
|
||||
if (!RawInstallCitronMeta(new_cnmt)) {
|
||||
@@ -788,7 +788,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
|
||||
id = *override_id;
|
||||
} else {
|
||||
const auto& data = in->ReadBytes(0x100000);
|
||||
mbedtls_sha256_ret(data.data(), data.size(), hash.data(), 0);
|
||||
{ unsigned int _sl = 32; EVP_Digest(data.data(), data.size(), hash.data(), &_sl, EVP_sha256(), nullptr); }
|
||||
memcpy(id.data(), hash.data(), 16);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include <mbedtls/md.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/hex_util.h"
|
||||
@@ -28,19 +28,11 @@ constexpr u64 NAX_HEADER_PADDING_SIZE = 0x4000;
|
||||
template <typename SourceData, typename SourceKey, typename Destination>
|
||||
static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t key_length,
|
||||
const SourceData* data, std::size_t data_length) {
|
||||
mbedtls_md_context_t context;
|
||||
mbedtls_md_init(&context);
|
||||
|
||||
if (mbedtls_md_setup(&context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1) ||
|
||||
mbedtls_md_hmac_starts(&context, reinterpret_cast<const u8*>(key), key_length) ||
|
||||
mbedtls_md_hmac_update(&context, reinterpret_cast<const u8*>(data), data_length) ||
|
||||
mbedtls_md_hmac_finish(&context, reinterpret_cast<u8*>(out))) {
|
||||
mbedtls_md_free(&context);
|
||||
return false;
|
||||
}
|
||||
|
||||
mbedtls_md_free(&context);
|
||||
return true;
|
||||
unsigned int out_len = sizeof(Destination);
|
||||
return HMAC(EVP_sha256(),
|
||||
reinterpret_cast<const unsigned char*>(key), static_cast<int>(key_length),
|
||||
reinterpret_cast<const unsigned char*>(data), data_length,
|
||||
reinterpret_cast<unsigned char*>(out), &out_len) != nullptr;
|
||||
}
|
||||
|
||||
NAX::NAX(VirtualFile file_)
|
||||
@@ -65,7 +57,8 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
|
||||
: header(std::make_unique<NAXHeader>()),
|
||||
file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0);
|
||||
unsigned int sha_len = static_cast<unsigned int>(hash.size());
|
||||
EVP_Digest(nca_id.data(), nca_id.size(), hash.data(), &sha_len, EVP_sha256(), nullptr);
|
||||
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
|
||||
Common::HexToString(nca_id, false)));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <mbedtls/md5.h>
|
||||
|
||||
#include "core/hle/service/bcat/bcat_result.h"
|
||||
#include "core/hle/service/bcat/bcat_types.h"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "core/hle/service/bcat/bcat_util.h"
|
||||
#include "core/hle/service/bcat/delivery_cache_directory_service.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include <openssl/evp.h>
|
||||
|
||||
namespace Service::BCAT {
|
||||
|
||||
@@ -15,7 +16,8 @@ namespace Service::BCAT {
|
||||
static BcatDigest DigestFile(const FileSys::VirtualFile& file) {
|
||||
BcatDigest out{};
|
||||
const auto bytes = file->ReadAllBytes();
|
||||
mbedtls_md5_ret(bytes.data(), bytes.size(), out.data());
|
||||
unsigned int out_len = static_cast<unsigned int>(out.size());
|
||||
EVP_Digest(bytes.data(), bytes.size(), out.data(), &out_len, EVP_md5(), nullptr);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <array>
|
||||
#include <mbedtls/aes.h>
|
||||
#include <mbedtls/hmac_drbg.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/params.h>
|
||||
#include "core/crypto/aes_ni.h"
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
@@ -14,6 +16,8 @@
|
||||
#include "common/logging.h"
|
||||
#include "core/hle/service/nfc/common/amiibo_crypto.h"
|
||||
|
||||
using namespace Core::Crypto;
|
||||
|
||||
namespace Service::NFP::AmiiboCrypto {
|
||||
|
||||
bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
|
||||
@@ -179,37 +183,43 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed
|
||||
return output;
|
||||
}
|
||||
|
||||
void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key,
|
||||
void CryptoInit(CryptoCtx& ctx, evp_mac_ctx_st*& hmac_ctx, const HmacKey& hmac_key,
|
||||
std::span<const u8> seed) {
|
||||
// Initialize context
|
||||
// Initialize counter/buffer context
|
||||
ctx.used = false;
|
||||
ctx.counter = 0;
|
||||
ctx.buffer_size = sizeof(ctx.counter) + seed.size();
|
||||
memcpy(ctx.buffer.data() + sizeof(u16), seed.data(), seed.size());
|
||||
|
||||
// Initialize HMAC context
|
||||
mbedtls_md_init(&hmac_ctx);
|
||||
mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1);
|
||||
mbedtls_md_hmac_starts(&hmac_ctx, hmac_key.data(), hmac_key.size());
|
||||
// Initialize OpenSSL HMAC-SHA256 context via EVP_MAC (non-deprecated API)
|
||||
EVP_MAC* mac = EVP_MAC_fetch(nullptr, "HMAC", nullptr);
|
||||
hmac_ctx = EVP_MAC_CTX_new(mac);
|
||||
EVP_MAC_free(mac); // CTX holds its own reference
|
||||
|
||||
OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_construct_utf8_string("digest", const_cast<char*>("SHA256"), 0),
|
||||
OSSL_PARAM_construct_end()
|
||||
};
|
||||
EVP_MAC_init(hmac_ctx, hmac_key.data(), hmac_key.size(), params);
|
||||
}
|
||||
|
||||
void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output) {
|
||||
// If used at least once, reinitialize the HMAC
|
||||
void CryptoStep(CryptoCtx& ctx, evp_mac_ctx_st* hmac_ctx, DrgbOutput& output) {
|
||||
// Reset HMAC for reuse (same key, new message) - NULL key re-uses the existing key
|
||||
if (ctx.used) {
|
||||
mbedtls_md_hmac_reset(&hmac_ctx);
|
||||
EVP_MAC_init(hmac_ctx, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
ctx.used = true;
|
||||
|
||||
// Store counter in big endian, and increment it
|
||||
// Store counter big-endian and advance
|
||||
ctx.buffer[0] = static_cast<u8>(ctx.counter >> 8);
|
||||
ctx.buffer[1] = static_cast<u8>(ctx.counter >> 0);
|
||||
ctx.counter++;
|
||||
|
||||
// Do HMAC magic
|
||||
mbedtls_md_hmac_update(&hmac_ctx, reinterpret_cast<const unsigned char*>(ctx.buffer.data()),
|
||||
ctx.buffer_size);
|
||||
mbedtls_md_hmac_finish(&hmac_ctx, output.data());
|
||||
EVP_MAC_update(hmac_ctx,
|
||||
reinterpret_cast<const unsigned char*>(ctx.buffer.data()),
|
||||
ctx.buffer_size);
|
||||
size_t outlen = output.size();
|
||||
EVP_MAC_final(hmac_ctx, output.data(), &outlen, output.size());
|
||||
}
|
||||
|
||||
DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data) {
|
||||
@@ -220,7 +230,7 @@ DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data) {
|
||||
|
||||
// Initialize context
|
||||
CryptoCtx ctx{};
|
||||
mbedtls_md_context_t hmac_ctx;
|
||||
evp_mac_ctx_st* hmac_ctx = nullptr;
|
||||
CryptoInit(ctx, hmac_ctx, key.hmac_key, internal_key);
|
||||
|
||||
// Generate derived keys
|
||||
@@ -230,27 +240,27 @@ DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data) {
|
||||
CryptoStep(ctx, hmac_ctx, temp[1]);
|
||||
memcpy(&derived_keys, temp.data(), sizeof(DerivedKeys));
|
||||
|
||||
// Cleanup context
|
||||
mbedtls_md_free(&hmac_ctx);
|
||||
// Cleanup
|
||||
EVP_MAC_CTX_free(hmac_ctx);
|
||||
|
||||
return derived_keys;
|
||||
}
|
||||
|
||||
void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data) {
|
||||
mbedtls_aes_context aes;
|
||||
std::size_t nc_off = 0;
|
||||
std::array<u8, sizeof(keys.aes_iv)> nonce_counter{};
|
||||
std::array<u8, sizeof(keys.aes_iv)> stream_block{};
|
||||
// Build the AES-128-CTR key schedule and run the single-pass intrinsic CTR.
|
||||
__m128i ks[AesNi::kRoundKeys128];
|
||||
AesNi::KeyExpand128Enc(keys.aes_key.data(), ks);
|
||||
|
||||
const auto aes_key_size = static_cast<u32>(keys.aes_key.size() * 8);
|
||||
mbedtls_aes_setkey_enc(&aes, keys.aes_key.data(), aes_key_size);
|
||||
memcpy(nonce_counter.data(), keys.aes_iv.data(), sizeof(keys.aes_iv));
|
||||
// Counter starts at the IV; copy to mutable array for CtrInc.
|
||||
uint8_t ctr[AesNi::kBlockSize];
|
||||
std::memcpy(ctr, keys.aes_iv.data(), AesNi::kBlockSize);
|
||||
|
||||
constexpr std::size_t encrypted_data_size = HMAC_TAG_START - SETTINGS_START;
|
||||
mbedtls_aes_crypt_ctr(&aes, encrypted_data_size, &nc_off, nonce_counter.data(),
|
||||
stream_block.data(),
|
||||
reinterpret_cast<const unsigned char*>(&in_data.settings),
|
||||
reinterpret_cast<unsigned char*>(&out_data.settings));
|
||||
AesNi::Ctr128(ks,
|
||||
reinterpret_cast<const uint8_t*>(&in_data.settings),
|
||||
reinterpret_cast<uint8_t*>(&out_data.settings),
|
||||
encrypted_data_size,
|
||||
ctr);
|
||||
|
||||
// Copy the rest of the data directly
|
||||
out_data.uid = in_data.uid;
|
||||
@@ -317,16 +327,16 @@ bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& t
|
||||
|
||||
// Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC!
|
||||
constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
|
||||
sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid),
|
||||
input_length, reinterpret_cast<unsigned char*>(&tag_data.hmac_tag));
|
||||
unsigned int hmac_len = sizeof(HashData);
|
||||
HMAC(EVP_sha256(), tag_keys.hmac_key.data(), static_cast<int>(sizeof(HmacKey)),
|
||||
reinterpret_cast<const unsigned char*>(&tag_data.uid), input_length,
|
||||
reinterpret_cast<unsigned char*>(&tag_data.hmac_tag), &hmac_len);
|
||||
|
||||
// Regenerate data HMAC
|
||||
constexpr std::size_t input_length2 = DYNAMIC_LOCK_START - WRITE_COUNTER_START;
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), data_keys.hmac_key.data(),
|
||||
sizeof(HmacKey),
|
||||
reinterpret_cast<const unsigned char*>(&tag_data.write_counter), input_length2,
|
||||
reinterpret_cast<unsigned char*>(&tag_data.hmac_data));
|
||||
HMAC(EVP_sha256(), data_keys.hmac_key.data(), static_cast<int>(sizeof(HmacKey)),
|
||||
reinterpret_cast<const unsigned char*>(&tag_data.write_counter), input_length2,
|
||||
reinterpret_cast<unsigned char*>(&tag_data.hmac_data), &hmac_len);
|
||||
|
||||
if (tag_data.hmac_data != encrypted_tag_data.user_memory.hmac_data) {
|
||||
LOG_ERROR(Service_NFP, "hmac_data doesn't match");
|
||||
@@ -358,27 +368,36 @@ bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_t
|
||||
// Generate tag HMAC
|
||||
constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
|
||||
constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START;
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
|
||||
sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid),
|
||||
input_length, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag));
|
||||
unsigned int hmac_len = sizeof(HashData);
|
||||
HMAC(EVP_sha256(), tag_keys.hmac_key.data(), static_cast<int>(sizeof(HmacKey)),
|
||||
reinterpret_cast<const unsigned char*>(&tag_data.uid), input_length,
|
||||
reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag), &hmac_len);
|
||||
|
||||
// Init mbedtls HMAC context
|
||||
mbedtls_md_context_t ctx;
|
||||
mbedtls_md_init(&ctx);
|
||||
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1);
|
||||
|
||||
// Generate data HMAC
|
||||
mbedtls_md_hmac_starts(&ctx, data_keys.hmac_key.data(), sizeof(HmacKey));
|
||||
mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.write_counter),
|
||||
input_length2); // Data
|
||||
mbedtls_md_hmac_update(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag),
|
||||
sizeof(HashData)); // Tag HMAC
|
||||
mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.uid),
|
||||
input_length);
|
||||
mbedtls_md_hmac_finish(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data));
|
||||
|
||||
// HMAC cleanup
|
||||
mbedtls_md_free(&ctx);
|
||||
// Generate data HMAC via EVP_MAC (multi-step: spans three non-contiguous buffers)
|
||||
{
|
||||
EVP_MAC* mac = EVP_MAC_fetch(nullptr, "HMAC", nullptr);
|
||||
EVP_MAC_CTX* ctx = EVP_MAC_CTX_new(mac);
|
||||
EVP_MAC_free(mac);
|
||||
OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_construct_utf8_string("digest", const_cast<char*>("SHA256"), 0),
|
||||
OSSL_PARAM_construct_end()
|
||||
};
|
||||
EVP_MAC_init(ctx, data_keys.hmac_key.data(), sizeof(HmacKey), params);
|
||||
EVP_MAC_update(ctx,
|
||||
reinterpret_cast<const unsigned char*>(&tag_data.write_counter),
|
||||
input_length2);
|
||||
EVP_MAC_update(ctx,
|
||||
reinterpret_cast<const unsigned char*>(&encoded_tag_data.hmac_tag),
|
||||
sizeof(HashData));
|
||||
EVP_MAC_update(ctx,
|
||||
reinterpret_cast<const unsigned char*>(&tag_data.uid),
|
||||
input_length);
|
||||
size_t outlen = sizeof(HashData);
|
||||
EVP_MAC_final(ctx,
|
||||
reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data),
|
||||
&outlen, sizeof(HashData));
|
||||
EVP_MAC_CTX_free(ctx);
|
||||
}
|
||||
|
||||
// Encrypt
|
||||
Cipher(data_keys, tag_data, encoded_tag_data);
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#include <array>
|
||||
|
||||
#include "core/hle/service/nfp/nfp_types.h"
|
||||
|
||||
struct mbedtls_md_context_t;
|
||||
// Forward declare OpenSSL EVP_MAC_CTX to avoid pulling in openssl/evp.h in the header.
|
||||
struct evp_mac_ctx_st;
|
||||
|
||||
namespace Service::NFP::AmiiboCrypto {
|
||||
// Byte locations in Service::NFP::NTAG215File
|
||||
@@ -73,12 +73,12 @@ HashSeed GetSeed(const NTAG215File& data);
|
||||
// Middle step on the generation of derived keys
|
||||
std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed);
|
||||
|
||||
// Initializes mbedtls context
|
||||
void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key,
|
||||
// Initializes OpenSSL HMAC-SHA256 context for key derivation
|
||||
void CryptoInit(CryptoCtx& ctx, evp_mac_ctx_st*& hmac_ctx, const HmacKey& hmac_key,
|
||||
std::span<const u8> seed);
|
||||
|
||||
// Feeds data to mbedtls context to generate the derived key
|
||||
void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output);
|
||||
// Feeds data into the HMAC context to generate the next derived key block
|
||||
void CryptoStep(CryptoCtx& ctx, evp_mac_ctx_st* hmac_ctx, DrgbOutput& output);
|
||||
|
||||
// Generates the derived key from amiibo data
|
||||
DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
@@ -178,7 +178,7 @@ struct ProcessContext {
|
||||
std::vector<u8> nro_data(size);
|
||||
m_process->GetMemory().ReadBlock(base_address, nro_data.data(), size);
|
||||
|
||||
mbedtls_sha256_ret(nro_data.data(), size, hash.data(), 0);
|
||||
{ unsigned int _sl = 32; EVP_Digest(nro_data.data(), size, hash.data(), &_sl, EVP_sha256(), nullptr); }
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < MaxNrrInfos; i++) {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/deconstructed_rom_directory.h"
|
||||
#include "core/loader/nca.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include <openssl/evp.h>
|
||||
|
||||
namespace Loader {
|
||||
|
||||
@@ -112,14 +112,13 @@ ResultStatus AppLoader_NCA::VerifyIntegrity(std::function<bool(size_t, size_t)>
|
||||
// Declare buffer to read into.
|
||||
std::vector<u8> buffer(4_MiB);
|
||||
|
||||
// Initialize sha256 verification context.
|
||||
mbedtls_sha256_context ctx;
|
||||
mbedtls_sha256_init(&ctx);
|
||||
mbedtls_sha256_starts_ret(&ctx, 0);
|
||||
// Initialize SHA-256 streaming context via OpenSSL EVP.
|
||||
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
|
||||
EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr);
|
||||
|
||||
// Ensure we maintain a clean state on exit.
|
||||
// Ensure we clean up on exit.
|
||||
SCOPE_EXIT {
|
||||
mbedtls_sha256_free(&ctx);
|
||||
EVP_MD_CTX_free(ctx);
|
||||
};
|
||||
|
||||
// Declare counters.
|
||||
@@ -133,7 +132,7 @@ ResultStatus AppLoader_NCA::VerifyIntegrity(std::function<bool(size_t, size_t)>
|
||||
const size_t read_size = file->Read(buffer.data(), intended_read_size, processed_size);
|
||||
|
||||
// Update the hash function with the buffer contents.
|
||||
mbedtls_sha256_update_ret(&ctx, buffer.data(), read_size);
|
||||
EVP_DigestUpdate(ctx, buffer.data(), read_size);
|
||||
|
||||
// Update counters.
|
||||
processed_size += read_size;
|
||||
@@ -146,7 +145,8 @@ ResultStatus AppLoader_NCA::VerifyIntegrity(std::function<bool(size_t, size_t)>
|
||||
|
||||
// Finalize context and compute the output hash.
|
||||
std::array<u8, NcaSha256HashLength> output_hash;
|
||||
mbedtls_sha256_finish_ret(&ctx, output_hash.data());
|
||||
unsigned int hash_len = static_cast<unsigned int>(output_hash.size());
|
||||
EVP_DigestFinal_ex(ctx, output_hash.data(), &hash_len);
|
||||
|
||||
// Compare to expected.
|
||||
if (std::memcmp(input_hash.data(), output_hash.data(), NcaSha256HalfHashLength) != 0) {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <shellapi.h>
|
||||
#endif
|
||||
|
||||
#include <mbedtls/base64.h>
|
||||
#include <openssl/evp.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/detached_tasks.h"
|
||||
#include "common/fs/file.h"
|
||||
@@ -76,17 +76,36 @@ static constexpr char BanListMagic[] = "CitronRoom-BanList-1";
|
||||
|
||||
static constexpr char token_delimiter{':'};
|
||||
|
||||
static void PadToken(std::string& token) {
|
||||
std::size_t outlen = 0;
|
||||
// Decode standard base64 (with padding). Returns number of decoded bytes or -1 on error.
|
||||
// EVP_DecodeBlock strips whitespace and handles trailing '=' padding.
|
||||
static int Base64Decode(unsigned char* out, int out_max,
|
||||
const unsigned char* in, int in_len) {
|
||||
if (in_len == 0) return 0;
|
||||
// EVP_DecodeBlock requires length to be a multiple of 4; pad if needed with a copy.
|
||||
std::string padded(reinterpret_cast<const char*>(in), static_cast<size_t>(in_len));
|
||||
while (padded.size() % 4 != 0) padded += '=';
|
||||
int n = EVP_DecodeBlock(out, reinterpret_cast<const unsigned char*>(padded.data()),
|
||||
static_cast<int>(padded.size()));
|
||||
if (n < 0) return -1;
|
||||
// Subtract padding bytes
|
||||
if (!padded.empty() && padded[padded.size()-1] == '=') n--;
|
||||
if (padded.size() >= 2 && padded[padded.size()-2] == '=') n--;
|
||||
return n;
|
||||
}
|
||||
|
||||
static void PadToken(std::string& token) {
|
||||
std::array<unsigned char, 512> output{};
|
||||
std::array<unsigned char, 2048> roundtrip{};
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
mbedtls_base64_decode(output.data(), output.size(), &outlen,
|
||||
reinterpret_cast<const unsigned char*>(token.c_str()),
|
||||
token.length());
|
||||
mbedtls_base64_encode(roundtrip.data(), roundtrip.size(), &outlen, output.data(), outlen);
|
||||
if (memcmp(roundtrip.data(), token.data(), token.size()) == 0) {
|
||||
int outlen = Base64Decode(output.data(), static_cast<int>(output.size()),
|
||||
reinterpret_cast<const unsigned char*>(token.c_str()),
|
||||
static_cast<int>(token.length()));
|
||||
if (outlen < 0) break;
|
||||
// EVP_EncodeBlock: produces standard base64 with '=' padding, no newlines
|
||||
int encoded_len = EVP_EncodeBlock(roundtrip.data(), output.data(), outlen);
|
||||
if (std::string_view(reinterpret_cast<char*>(roundtrip.data()),
|
||||
static_cast<size_t>(encoded_len)) ==
|
||||
std::string_view(token.data(), token.size())) {
|
||||
break;
|
||||
}
|
||||
token.push_back('=');
|
||||
@@ -94,25 +113,25 @@ static void PadToken(std::string& token) {
|
||||
}
|
||||
|
||||
static std::string UsernameFromDisplayToken(const std::string& display_token) {
|
||||
std::size_t outlen;
|
||||
|
||||
std::array<unsigned char, 512> output{};
|
||||
mbedtls_base64_decode(output.data(), output.size(), &outlen,
|
||||
reinterpret_cast<const unsigned char*>(display_token.c_str()),
|
||||
display_token.length());
|
||||
std::string decoded_display_token(reinterpret_cast<char*>(&output), outlen);
|
||||
return decoded_display_token.substr(0, decoded_display_token.find(token_delimiter));
|
||||
int outlen = Base64Decode(output.data(), static_cast<int>(output.size()),
|
||||
reinterpret_cast<const unsigned char*>(display_token.c_str()),
|
||||
static_cast<int>(display_token.length()));
|
||||
if (outlen < 0) return {};
|
||||
std::string decoded(reinterpret_cast<char*>(output.data()), static_cast<size_t>(outlen));
|
||||
return decoded.substr(0, decoded.find(token_delimiter));
|
||||
}
|
||||
|
||||
static std::string TokenFromDisplayToken(const std::string& display_token) {
|
||||
std::size_t outlen;
|
||||
|
||||
std::array<unsigned char, 512> output{};
|
||||
mbedtls_base64_decode(output.data(), output.size(), &outlen,
|
||||
reinterpret_cast<const unsigned char*>(display_token.c_str()),
|
||||
display_token.length());
|
||||
std::string decoded_display_token(reinterpret_cast<char*>(&output), outlen);
|
||||
return decoded_display_token.substr(decoded_display_token.find(token_delimiter) + 1);
|
||||
int outlen = Base64Decode(output.data(), static_cast<int>(output.size()),
|
||||
reinterpret_cast<const unsigned char*>(display_token.c_str()),
|
||||
static_cast<int>(display_token.length()));
|
||||
if (outlen < 0) return {};
|
||||
std::string decoded(reinterpret_cast<char*>(output.data()), static_cast<size_t>(outlen));
|
||||
const auto pos = decoded.find(token_delimiter);
|
||||
if (pos == std::string::npos) return {};
|
||||
return decoded.substr(pos + 1);
|
||||
}
|
||||
|
||||
static Network::Room::BanList LoadBanList(const std::string& path) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user