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