From b34847d59ecb2b87f6dd0f984b32177003101497 Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Mon, 15 Apr 2019 22:56:55 +0800 Subject: [PATCH] service/ps: Implement PS:EncryptDecryptAES --- src/common/logging/backend.cpp | 1 + src/common/logging/log.h | 1 + src/core/hle/service/ps/ps_ps.cpp | 128 +++++++++++++++++++++++++++++- src/core/hle/service/service.cpp | 2 +- src/core/hw/aes/arithmetic128.cpp | 14 ++++ src/core/hw/aes/arithmetic128.h | 1 + 6 files changed, 145 insertions(+), 2 deletions(-) diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index d440d910a..b6b71fdfc 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -196,6 +196,7 @@ void DebuggerBackend::Write(const Entry& entry) { SUB(Service, SOC) \ SUB(Service, IR) \ SUB(Service, Y2R) \ + SUB(Service, PS) \ CLS(HW) \ SUB(HW, Memory) \ SUB(HW, LCD) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index fe529f415..380c8ad5a 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -82,6 +82,7 @@ enum class Class : ClassType { Service_SOC, ///< The SOC (Socket) service Service_IR, ///< The IR service Service_Y2R, ///< The Y2R (YUV to RGB conversion) service + Service_PS, ///< The PS (Process) service HW, ///< Low-level hardware emulation HW_Memory, ///< Memory-map and address translation HW_LCD, ///< LCD register emulation diff --git a/src/core/hle/service/ps/ps_ps.cpp b/src/core/hle/service/ps/ps_ps.cpp index e8d4d733c..e79ad204f 100644 --- a/src/core/hle/service/ps/ps_ps.cpp +++ b/src/core/hle/service/ps/ps_ps.cpp @@ -2,18 +2,144 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include +#include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/ps/ps_ps.h" +#include "core/hw/aes/arithmetic128.h" +#include "core/hw/aes/key.h" namespace Service::PS { +enum class AlgorithmType : u8 { + CBC_Encrypt, + CBC_Decrypt, + CTR_Encrypt, + CTR_Decrypt, + CCM_Encrypt, + CCM_Decrypt, +}; + +constexpr std::array KeyTypes{{ + 0x0D, + 0x2D, + 0x31, + 0x38, + 0x32, + 0x39, + 0x2E, + 0, /* invalid */ + 0x36, + 0x39, +}}; + +void PS_PS::EncryptDecryptAes(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x4, 8, 4); + u32 src_size = rp.Pop(); + u32 dest_size = rp.Pop(); + + using CryptoPP::AES; + std::array iv; + rp.PopRaw(iv); + + AlgorithmType algorithm = rp.PopEnum(); + u8 key_type = rp.Pop(); + auto source = rp.PopMappedBuffer(); + auto destination = rp.PopMappedBuffer(); + + LOG_DEBUG(Service_PS, "called algorithm={} key_type={}", static_cast(algorithm), key_type); + + // TODO(zhaowenlan1779): Tests on a real 3DS shows that no error is returned in this case + // and encrypted data is actually returned, but the key used is unknown. + ASSERT_MSG(key_type != 7 && key_type < 10, "Key type is invalid"); + + if (!HW::AES::IsNormalKeyAvailable(KeyTypes[key_type])) { + LOG_ERROR(Service_PS, + "Key 0x{:2X} is not available, encryption/decryption will not be correct", + KeyTypes[key_type]); + } + + HW::AES::AESKey key = HW::AES::GetNormalKey(KeyTypes[key_type]); + + if (algorithm == AlgorithmType::CCM_Encrypt || algorithm == AlgorithmType::CCM_Decrypt) { + // AES-CCM is not supported with this function + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::InvalidSection, ErrorModule::PS, + ErrorSummary::WrongArgument, ErrorLevel::Status)); + return; + } + + if (algorithm == AlgorithmType::CBC_Encrypt || algorithm == AlgorithmType::CBC_Decrypt) { + src_size &= 0xFFFFFFF0; // Clear the lowest 4 bits of the size (make it a multiple of 16) + ASSERT(src_size > 0); // Real 3DS calls svcBreak in this case + } + + std::vector src_buffer(src_size); + source.Read(src_buffer.data(), 0, src_buffer.size()); + + std::vector dst_buffer(src_buffer.size()); + switch (algorithm) { + case AlgorithmType::CTR_Encrypt: { + CryptoPP::CTR_Mode::Encryption aes; + aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, iv.data()); + aes.ProcessData(dst_buffer.data(), src_buffer.data(), src_buffer.size()); + break; + } + + case AlgorithmType::CTR_Decrypt: { + CryptoPP::CTR_Mode::Decryption aes; + aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, iv.data()); + aes.ProcessData(dst_buffer.data(), src_buffer.data(), src_buffer.size()); + break; + } + + case AlgorithmType::CBC_Encrypt: { + CryptoPP::CBC_Mode::Encryption aes; + aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, iv.data()); + aes.ProcessData(dst_buffer.data(), src_buffer.data(), src_buffer.size()); + break; + } + + case AlgorithmType::CBC_Decrypt: { + CryptoPP::CBC_Mode::Decryption aes; + aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, iv.data()); + aes.ProcessData(dst_buffer.data(), src_buffer.data(), src_buffer.size()); + break; + } + + default: + UNREACHABLE(); + } + + destination.Write(dst_buffer.data(), 0, dst_buffer.size()); + + // We will need to calculate the resulting IV/CTR ourselves as CrytoPP does not + // provide an easy way to get them + std::array new_iv; + if (algorithm == AlgorithmType::CTR_Encrypt || algorithm == AlgorithmType::CTR_Decrypt) { + new_iv = HW::AES::Add128(iv, src_size / 16); + } else if (algorithm == AlgorithmType::CBC_Encrypt) { // For AES-CBC, The new IV is the last + // block of ciphertext + std::copy_n(dst_buffer.end() - new_iv.size(), new_iv.size(), new_iv.begin()); + } else { + std::copy_n(src_buffer.end() - new_iv.size(), new_iv.size(), new_iv.begin()); + } + + IPC::RequestBuilder rb = rp.MakeBuilder(5, 4); + rb.Push(RESULT_SUCCESS); + rb.PushRaw(new_iv); + rb.PushMappedBuffer(source); + rb.PushMappedBuffer(destination); +} + PS_PS::PS_PS() : ServiceFramework("ps:ps", DefaultMaxSessions) { static const FunctionInfo functions[] = { // clang-format off {0x00010244, nullptr, "SignRsaSha256"}, {0x00020244, nullptr, "VerifyRsaSha256"}, - {0x00040204, nullptr, "EncryptDecryptAes"}, + {0x00040204, &PS_PS::EncryptDecryptAes, "EncryptDecryptAes"}, {0x00050284, nullptr, "EncryptSignDecryptVerifyAesCcm"}, {0x00060040, nullptr, "GetRomId"}, {0x00070040, nullptr, "GetRomId2"}, diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index a4ccf429e..7b7a670fe 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -98,6 +98,7 @@ const std::array service_module_map{ {"HTTP", 0x00040130'00002902, HTTP::InstallInterfaces}, {"SOC", 0x00040130'00002E02, SOC::InstallInterfaces}, {"SSL", 0x00040130'00002F02, SSL::InstallInterfaces}, + {"PS", 0x00040130'00003102, PS::InstallInterfaces}, // no HLE implementation {"CDC", 0x00040130'00001802, nullptr}, {"GPIO", 0x00040130'00001B02, nullptr}, @@ -105,7 +106,6 @@ const std::array service_module_map{ {"MCU", 0x00040130'00001F02, nullptr}, {"MP", 0x00040130'00002A02, nullptr}, {"PDN", 0x00040130'00002102, nullptr}, - {"PS", 0x00040130'00003102, nullptr}, {"SPI", 0x00040130'00002302, nullptr}}}; /** diff --git a/src/core/hw/aes/arithmetic128.cpp b/src/core/hw/aes/arithmetic128.cpp index 6a1308f2d..649f82a9b 100644 --- a/src/core/hw/aes/arithmetic128.cpp +++ b/src/core/hw/aes/arithmetic128.cpp @@ -36,6 +36,20 @@ AESKey Add128(const AESKey& a, const AESKey& b) { return out; } +AESKey Add128(const AESKey& a, u64 b) { + AESKey out = a; + u32 carry = 0; + u32 sum = 0; + + for (int i = 15; i >= 8; i--) { + sum = a[i] + static_cast((b >> ((15 - i) * 8)) & 0xff) + carry; + carry = sum >> 8; + out[i] = static_cast(sum & 0xff); + } + + return out; +} + AESKey Xor128(const AESKey& a, const AESKey& b) { AESKey out; std::transform(a.cbegin(), a.cend(), b.cbegin(), out.begin(), std::bit_xor<>()); diff --git a/src/core/hw/aes/arithmetic128.h b/src/core/hw/aes/arithmetic128.h index 62d110e16..6223d618e 100644 --- a/src/core/hw/aes/arithmetic128.h +++ b/src/core/hw/aes/arithmetic128.h @@ -10,6 +10,7 @@ namespace HW::AES { AESKey Lrot128(const AESKey& in, u32 rot); AESKey Add128(const AESKey& a, const AESKey& b); +AESKey Add128(const AESKey& a, u64 b); AESKey Xor128(const AESKey& a, const AESKey& b); } // namespace HW::AES