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:
cheezwiz7899
2026-06-11 05:37:48 +10:00
parent 16bff11834
commit 7acd3dd394
22 changed files with 1170 additions and 333 deletions
+1 -1
View File
@@ -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)
+8 -36
View File
@@ -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)
+22 -1
View File
@@ -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
)
+5 -19
View File
@@ -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)
+1 -1
View File
@@ -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
View File
@@ -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
+3 -3
View File
@@ -14,9 +14,9 @@ namespace Core::Crypto {
struct CipherContext;
enum class Mode {
CTR = 11,
ECB = 2,
XTS = 70,
CTR,
ECB,
XTS,
};
enum class Op {
+25 -26
View File
@@ -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"
-1
View File
@@ -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;
+6 -6
View File
@@ -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);
}
+9 -16
View File
@@ -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)));
}
-1
View File
@@ -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);
+2 -2
View File
@@ -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++) {
+9 -9
View File
@@ -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) {
+41 -22
View File
@@ -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