mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	service/ps: Implement PS:EncryptDecryptAES
This commit is contained in:
		
							parent
							
								
									14730ed560
								
							
						
					
					
						commit
						b34847d59e
					
				
					 6 changed files with 145 additions and 2 deletions
				
			
		|  | @ -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)                                                                                   \ | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -2,18 +2,144 @@ | |||
| // 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{{ | ||||
|     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>(); | ||||
|     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, 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<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"}, | ||||
|  |  | |||
|  | @ -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}}}; | ||||
| 
 | ||||
| /**
 | ||||
|  |  | |||
|  | @ -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<>()); | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue