Merge pull request #4733 from zhaowenlan1779/ps

service/ps: Implement PS:EncryptDecryptAes
This commit is contained in:
Pengfei Zhu 2019-04-29 21:39:53 +08:00 committed by GitHub
commit 37861526ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 164 additions and 2 deletions

View file

@ -220,6 +220,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) \

View file

@ -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

View file

@ -2,18 +2,148 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#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<u8, 10> KeyTypes{{
HW::AES::SSLKey,
HW::AES::UDSDataKey,
HW::AES::APTWrap,
HW::AES::BOSSDataKey,
0x32, // unknown
HW::AES::DLPDataKey,
HW::AES::CECDDataKey,
0, // invalid
HW::AES::FRDKey,
// Note: According to 3dbrew the KeyY is overridden by Process9 when using this key type.
// TODO: implement this behaviour?
HW::AES::NFCKey,
}};
void PS_PS::EncryptDecryptAes(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x4, 8, 4);
u32 src_size = rp.Pop<u32>();
u32 dest_size = rp.Pop<u32>();
using CryptoPP::AES;
std::array<u8, AES::BLOCKSIZE> iv;
rp.PopRaw(iv);
AlgorithmType algorithm = rp.PopEnum<AlgorithmType>();
u8 key_type = rp.Pop<u8>();
auto source = rp.PopMappedBuffer();
auto destination = rp.PopMappedBuffer();
LOG_DEBUG(Service_PS, "called algorithm={} key_type={}", static_cast<u8>(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, 4);
rb.Push(ResultCode(ErrorDescription::InvalidSection, ErrorModule::PS,
ErrorSummary::WrongArgument, ErrorLevel::Status));
rb.PushMappedBuffer(source);
rb.PushMappedBuffer(destination);
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<u8> src_buffer(src_size);
source.Read(src_buffer.data(), 0, src_buffer.size());
std::vector<u8> dst_buffer(src_buffer.size());
switch (algorithm) {
case AlgorithmType::CTR_Encrypt: {
CryptoPP::CTR_Mode<AES>::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<AES>::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<AES>::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<AES>::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<u8, AES::BLOCKSIZE> 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"},

View file

@ -98,6 +98,7 @@ const std::array<ServiceModuleInfo, 40> 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<ServiceModuleInfo, 40> service_module_map{
{"MCU", 0x00040130'00001F02, nullptr},
{"MP", 0x00040130'00002A02, nullptr},
{"PDN", 0x00040130'00002102, nullptr},
{"PS", 0x00040130'00003102, nullptr},
{"SPI", 0x00040130'00002302, nullptr}}};
/**

View file

@ -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<u8>((b >> ((15 - i) * 8)) & 0xff) + carry;
carry = sum >> 8;
out[i] = static_cast<u8>(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<>());

View file

@ -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

View file

@ -24,6 +24,21 @@ enum KeySlotID : std::size_t {
// AES Keyslot used to generate the UDS data frame CCMP key.
UDSDataKey = 0x2D,
// AES Keyslot used to encrypt the BOSS container data.
BOSSDataKey = 0x38,
// AES Keyslot used to calculate DLP data frame checksum.
DLPDataKey = 0x39,
// AES Keyslot used to generate the StreetPass CCMP key.
CECDDataKey = 0x2E,
// AES Keyslot used by the friends module.
FRDKey = 0x36,
// AES Keyslot used by the NFC module.
NFCKey = 0x39,
// AES keyslot used for APT:Wrap/Unwrap functions
APTWrap = 0x31,