1 // Copyright 2018 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4
5 #include <cryptopp/aes.h>
6 #include <cryptopp/modes.h>
7 #include "common/archives.h"
8 #include "common/logging/log.h"
9 #include "core/core.h"
10 #include "core/hle/ipc_helpers.h"
11 #include "core/hle/service/ps/ps_ps.h"
12 #include "core/hw/aes/arithmetic128.h"
13 #include "core/hw/aes/key.h"
14
15 SERIALIZE_EXPORT_IMPL(Service::PS::PS_PS)
16
17 namespace Service::PS {
18
19 enum class AlgorithmType : u8 {
20 CBC_Encrypt,
21 CBC_Decrypt,
22 CTR_Encrypt,
23 CTR_Decrypt,
24 CCM_Encrypt,
25 CCM_Decrypt,
26 };
27
28 constexpr std::array<u8, 10> KeyTypes{{
29 HW::AES::SSLKey,
30 HW::AES::UDSDataKey,
31 HW::AES::APTWrap,
32 HW::AES::BOSSDataKey,
33 0x32, // unknown
34 HW::AES::DLPDataKey,
35 HW::AES::CECDDataKey,
36 0, // invalid
37 HW::AES::FRDKey,
38 // Note: According to 3dbrew the KeyY is overridden by Process9 when using this key type.
39 // TODO: implement this behaviour?
40 HW::AES::NFCKey,
41 }};
42
EncryptDecryptAes(Kernel::HLERequestContext & ctx)43 void PS_PS::EncryptDecryptAes(Kernel::HLERequestContext& ctx) {
44 IPC::RequestParser rp(ctx, 0x4, 8, 4);
45 auto src_size = rp.Pop<u32>();
46 [[maybe_unused]] const auto dest_size = rp.Pop<u32>();
47
48 using CryptoPP::AES;
49 std::array<u8, AES::BLOCKSIZE> iv;
50 rp.PopRaw(iv);
51
52 AlgorithmType algorithm = rp.PopEnum<AlgorithmType>();
53 u8 key_type = rp.Pop<u8>();
54 auto source = rp.PopMappedBuffer();
55 auto destination = rp.PopMappedBuffer();
56
57 LOG_DEBUG(Service_PS, "called algorithm={} key_type={}", algorithm, key_type);
58
59 // TODO(zhaowenlan1779): Tests on a real 3DS shows that no error is returned in this case
60 // and encrypted data is actually returned, but the key used is unknown.
61 ASSERT_MSG(key_type != 7 && key_type < 10, "Key type is invalid");
62
63 if (!HW::AES::IsNormalKeyAvailable(KeyTypes[key_type])) {
64 LOG_ERROR(Service_PS,
65 "Key 0x{:2X} is not available, encryption/decryption will not be correct",
66 KeyTypes[key_type]);
67 }
68
69 HW::AES::AESKey key = HW::AES::GetNormalKey(KeyTypes[key_type]);
70
71 if (algorithm == AlgorithmType::CCM_Encrypt || algorithm == AlgorithmType::CCM_Decrypt) {
72 // AES-CCM is not supported with this function
73 IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
74 rb.Push(ResultCode(ErrorDescription::InvalidSection, ErrorModule::PS,
75 ErrorSummary::WrongArgument, ErrorLevel::Status));
76 rb.PushMappedBuffer(source);
77 rb.PushMappedBuffer(destination);
78 return;
79 }
80
81 if (algorithm == AlgorithmType::CBC_Encrypt || algorithm == AlgorithmType::CBC_Decrypt) {
82 src_size &= 0xFFFFFFF0; // Clear the lowest 4 bits of the size (make it a multiple of 16)
83 ASSERT(src_size > 0); // Real 3DS calls svcBreak in this case
84 }
85
86 std::vector<u8> src_buffer(src_size);
87 source.Read(src_buffer.data(), 0, src_buffer.size());
88
89 std::vector<u8> dst_buffer(src_buffer.size());
90 switch (algorithm) {
91 case AlgorithmType::CTR_Encrypt: {
92 CryptoPP::CTR_Mode<AES>::Encryption aes;
93 aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, iv.data());
94 aes.ProcessData(dst_buffer.data(), src_buffer.data(), src_buffer.size());
95 break;
96 }
97
98 case AlgorithmType::CTR_Decrypt: {
99 CryptoPP::CTR_Mode<AES>::Decryption aes;
100 aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, iv.data());
101 aes.ProcessData(dst_buffer.data(), src_buffer.data(), src_buffer.size());
102 break;
103 }
104
105 case AlgorithmType::CBC_Encrypt: {
106 CryptoPP::CBC_Mode<AES>::Encryption aes;
107 aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, iv.data());
108 aes.ProcessData(dst_buffer.data(), src_buffer.data(), src_buffer.size());
109 break;
110 }
111
112 case AlgorithmType::CBC_Decrypt: {
113 CryptoPP::CBC_Mode<AES>::Decryption aes;
114 aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, iv.data());
115 aes.ProcessData(dst_buffer.data(), src_buffer.data(), src_buffer.size());
116 break;
117 }
118
119 default:
120 UNREACHABLE();
121 }
122
123 destination.Write(dst_buffer.data(), 0, dst_buffer.size());
124
125 // We will need to calculate the resulting IV/CTR ourselves as CrytoPP does not
126 // provide an easy way to get them
127 std::array<u8, AES::BLOCKSIZE> new_iv;
128 if (algorithm == AlgorithmType::CTR_Encrypt || algorithm == AlgorithmType::CTR_Decrypt) {
129 new_iv = HW::AES::Add128(iv, src_size / 16);
130 } else if (algorithm == AlgorithmType::CBC_Encrypt) {
131 // For AES-CBC, The new IV is the last block of ciphertext
132 std::copy_n(dst_buffer.end() - new_iv.size(), new_iv.size(), new_iv.begin());
133 } else {
134 std::copy_n(src_buffer.end() - new_iv.size(), new_iv.size(), new_iv.begin());
135 }
136
137 IPC::RequestBuilder rb = rp.MakeBuilder(5, 4);
138 rb.Push(RESULT_SUCCESS);
139 rb.PushRaw(new_iv);
140 rb.PushMappedBuffer(source);
141 rb.PushMappedBuffer(destination);
142 }
143
PS_PS()144 PS_PS::PS_PS() : ServiceFramework("ps:ps", DefaultMaxSessions) {
145 static const FunctionInfo functions[] = {
146 // clang-format off
147 {0x00010244, nullptr, "SignRsaSha256"},
148 {0x00020244, nullptr, "VerifyRsaSha256"},
149 {0x00040204, &PS_PS::EncryptDecryptAes, "EncryptDecryptAes"},
150 {0x00050284, nullptr, "EncryptSignDecryptVerifyAesCcm"},
151 {0x00060040, nullptr, "GetRomId"},
152 {0x00070040, nullptr, "GetRomId2"},
153 {0x00080040, nullptr, "GetRomMakerCode"},
154 {0x00090000, nullptr, "GetCTRCardAutoStartupBit"},
155 {0x000A0000, nullptr, "GetLocalFriendCodeSeed"},
156 {0x000B0000, nullptr, "GetDeviceId"},
157 {0x000C0000, nullptr, "SeedRNG"},
158 {0x000D0042, nullptr, "GenerateRandomBytes"},
159 {0x000E0082, nullptr, "InterfaceForPXI_0x04010084"},
160 {0x000F0082, nullptr, "InterfaceForPXI_0x04020082"},
161 {0x00100042, nullptr, "InterfaceForPXI_0x04030044"},
162 {0x00110042, nullptr, "InterfaceForPXI_0x04040044"},
163 // clang-format on
164 };
165
166 RegisterHandlers(functions);
167 };
168
InstallInterfaces(Core::System & system)169 void InstallInterfaces(Core::System& system) {
170 auto& service_manager = system.ServiceManager();
171 std::make_shared<PS_PS>()->InstallAsService(service_manager);
172 }
173
174 } // namespace Service::PS
175