mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-03 16:39:01 -06:00 
			
		
		
		
	Merge pull request #2569 from wwylele/wrap-unwrap
APT: implemented Wrap and Unwrap
This commit is contained in:
		@@ -18,6 +18,8 @@
 | 
			
		||||
#include "core/hle/service/fs/archive.h"
 | 
			
		||||
#include "core/hle/service/ptm/ptm.h"
 | 
			
		||||
#include "core/hle/service/service.h"
 | 
			
		||||
#include "core/hw/aes/ccm.h"
 | 
			
		||||
#include "core/hw/aes/key.h"
 | 
			
		||||
 | 
			
		||||
namespace Service {
 | 
			
		||||
namespace APT {
 | 
			
		||||
@@ -470,6 +472,107 @@ void GetStartupArgument(Service::Interface* self) {
 | 
			
		||||
    cmd_buff[2] = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Wrap(Service::Interface* self) {
 | 
			
		||||
    IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x46, 4, 4);
 | 
			
		||||
    const u32 output_size = rp.Pop<u32>();
 | 
			
		||||
    const u32 input_size = rp.Pop<u32>();
 | 
			
		||||
    const u32 nonce_offset = rp.Pop<u32>();
 | 
			
		||||
    u32 nonce_size = rp.Pop<u32>();
 | 
			
		||||
    size_t desc_size;
 | 
			
		||||
    IPC::MappedBufferPermissions desc_permission;
 | 
			
		||||
    const VAddr input = rp.PopMappedBuffer(&desc_size, &desc_permission);
 | 
			
		||||
    ASSERT(desc_size == input_size && desc_permission == IPC::MappedBufferPermissions::R);
 | 
			
		||||
    const VAddr output = rp.PopMappedBuffer(&desc_size, &desc_permission);
 | 
			
		||||
    ASSERT(desc_size == output_size && desc_permission == IPC::MappedBufferPermissions::W);
 | 
			
		||||
 | 
			
		||||
    // Note: real 3DS still returns SUCCESS when the sizes don't match. It seems that it doesn't
 | 
			
		||||
    // check the buffer size and writes data with potential overflow.
 | 
			
		||||
    ASSERT_MSG(output_size == input_size + HW::AES::CCM_MAC_SIZE,
 | 
			
		||||
               "input_size (%d) doesn't match to output_size (%d)", input_size, output_size);
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Service_APT, "called, output_size=%u, input_size=%u, nonce_offset=%u, nonce_size=%u",
 | 
			
		||||
              output_size, input_size, nonce_offset, nonce_size);
 | 
			
		||||
 | 
			
		||||
    // Note: This weird nonce size modification is verified against real 3DS
 | 
			
		||||
    nonce_size = std::min<u32>(nonce_size & ~3, HW::AES::CCM_NONCE_SIZE);
 | 
			
		||||
 | 
			
		||||
    // Reads nonce and concatenates the rest of the input as plaintext
 | 
			
		||||
    HW::AES::CCMNonce nonce{};
 | 
			
		||||
    Memory::ReadBlock(input + nonce_offset, nonce.data(), nonce_size);
 | 
			
		||||
    u32 pdata_size = input_size - nonce_size;
 | 
			
		||||
    std::vector<u8> pdata(pdata_size);
 | 
			
		||||
    Memory::ReadBlock(input, pdata.data(), nonce_offset);
 | 
			
		||||
    Memory::ReadBlock(input + nonce_offset + nonce_size, pdata.data() + nonce_offset,
 | 
			
		||||
                      pdata_size - nonce_offset);
 | 
			
		||||
 | 
			
		||||
    // Encrypts the plaintext using AES-CCM
 | 
			
		||||
    auto cipher = HW::AES::EncryptSignCCM(pdata, nonce, HW::AES::KeySlotID::APTWrap);
 | 
			
		||||
 | 
			
		||||
    // Puts the nonce to the beginning of the output, with ciphertext followed
 | 
			
		||||
    Memory::WriteBlock(output, nonce.data(), nonce_size);
 | 
			
		||||
    Memory::WriteBlock(output + nonce_size, cipher.data(), cipher.size());
 | 
			
		||||
 | 
			
		||||
    IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
 | 
			
		||||
    // Unmap buffer
 | 
			
		||||
    rb.PushMappedBuffer(input, input_size, IPC::MappedBufferPermissions::R);
 | 
			
		||||
    rb.PushMappedBuffer(output, output_size, IPC::MappedBufferPermissions::W);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Unwrap(Service::Interface* self) {
 | 
			
		||||
    IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x47, 4, 4);
 | 
			
		||||
    const u32 output_size = rp.Pop<u32>();
 | 
			
		||||
    const u32 input_size = rp.Pop<u32>();
 | 
			
		||||
    const u32 nonce_offset = rp.Pop<u32>();
 | 
			
		||||
    u32 nonce_size = rp.Pop<u32>();
 | 
			
		||||
    size_t desc_size;
 | 
			
		||||
    IPC::MappedBufferPermissions desc_permission;
 | 
			
		||||
    const VAddr input = rp.PopMappedBuffer(&desc_size, &desc_permission);
 | 
			
		||||
    ASSERT(desc_size == input_size && desc_permission == IPC::MappedBufferPermissions::R);
 | 
			
		||||
    const VAddr output = rp.PopMappedBuffer(&desc_size, &desc_permission);
 | 
			
		||||
    ASSERT(desc_size == output_size && desc_permission == IPC::MappedBufferPermissions::W);
 | 
			
		||||
 | 
			
		||||
    // Note: real 3DS still returns SUCCESS when the sizes don't match. It seems that it doesn't
 | 
			
		||||
    // check the buffer size and writes data with potential overflow.
 | 
			
		||||
    ASSERT_MSG(output_size == input_size - HW::AES::CCM_MAC_SIZE,
 | 
			
		||||
               "input_size (%d) doesn't match to output_size (%d)", input_size, output_size);
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Service_APT, "called, output_size=%u, input_size=%u, nonce_offset=%u, nonce_size=%u",
 | 
			
		||||
              output_size, input_size, nonce_offset, nonce_size);
 | 
			
		||||
 | 
			
		||||
    // Note: This weird nonce size modification is verified against real 3DS
 | 
			
		||||
    nonce_size = std::min<u32>(nonce_size & ~3, HW::AES::CCM_NONCE_SIZE);
 | 
			
		||||
 | 
			
		||||
    // Reads nonce and cipher text
 | 
			
		||||
    HW::AES::CCMNonce nonce{};
 | 
			
		||||
    Memory::ReadBlock(input, nonce.data(), nonce_size);
 | 
			
		||||
    u32 cipher_size = input_size - nonce_size;
 | 
			
		||||
    std::vector<u8> cipher(cipher_size);
 | 
			
		||||
    Memory::ReadBlock(input + nonce_size, cipher.data(), cipher_size);
 | 
			
		||||
 | 
			
		||||
    // Decrypts the ciphertext using AES-CCM
 | 
			
		||||
    auto pdata = HW::AES::DecryptVerifyCCM(cipher, nonce, HW::AES::KeySlotID::APTWrap);
 | 
			
		||||
 | 
			
		||||
    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 | 
			
		||||
    if (!pdata.empty()) {
 | 
			
		||||
        // Splits the plaintext and put the nonce in between
 | 
			
		||||
        Memory::WriteBlock(output, pdata.data(), nonce_offset);
 | 
			
		||||
        Memory::WriteBlock(output + nonce_offset, nonce.data(), nonce_size);
 | 
			
		||||
        Memory::WriteBlock(output + nonce_offset + nonce_size, pdata.data() + nonce_offset,
 | 
			
		||||
                           pdata.size() - nonce_offset);
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_ERROR(Service_APT, "Failed to decrypt data");
 | 
			
		||||
        rb.Push(ResultCode(static_cast<ErrorDescription>(1), ErrorModule::PS,
 | 
			
		||||
                           ErrorSummary::WrongArgument, ErrorLevel::Status));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Unmap buffer
 | 
			
		||||
    rb.PushMappedBuffer(input, input_size, IPC::MappedBufferPermissions::R);
 | 
			
		||||
    rb.PushMappedBuffer(output, output_size, IPC::MappedBufferPermissions::W);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CheckNew3DSApp(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -136,6 +136,46 @@ void Initialize(Service::Interface* self);
 | 
			
		||||
 */
 | 
			
		||||
void GetSharedFont(Service::Interface* self);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * APT::Wrap service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      1 : Output buffer size
 | 
			
		||||
 *      2 : Input buffer size
 | 
			
		||||
 *      3 : Nonce offset to the input buffer
 | 
			
		||||
 *      4 : Nonce size
 | 
			
		||||
 *      5 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xA)
 | 
			
		||||
 *      6 : Input buffer address
 | 
			
		||||
 *      7 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xC)
 | 
			
		||||
 *      8 : Output buffer address
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
 *      2 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xA)
 | 
			
		||||
 *      3 : Input buffer address
 | 
			
		||||
 *      4 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xC)
 | 
			
		||||
 *      5 : Output buffer address
 | 
			
		||||
 */
 | 
			
		||||
void Wrap(Service::Interface* self);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * APT::Unwrap service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      1 : Output buffer size
 | 
			
		||||
 *      2 : Input buffer size
 | 
			
		||||
 *      3 : Nonce offset to the output buffer
 | 
			
		||||
 *      4 : Nonce size
 | 
			
		||||
 *      5 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xA)
 | 
			
		||||
 *      6 : Input buffer address
 | 
			
		||||
 *      7 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xC)
 | 
			
		||||
 *      8 : Output buffer address
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
 *      2 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xA)
 | 
			
		||||
 *      3 : Input buffer address
 | 
			
		||||
 *      4 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xC)
 | 
			
		||||
 *      5 : Output buffer address
 | 
			
		||||
 */
 | 
			
		||||
void Unwrap(Service::Interface* self);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * APT::NotifyToWait service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 
 | 
			
		||||
@@ -78,8 +78,8 @@ const Interface::FunctionInfo FunctionTable[] = {
 | 
			
		||||
    {0x00430040, NotifyToWait, "NotifyToWait"},
 | 
			
		||||
    {0x00440000, GetSharedFont, "GetSharedFont"},
 | 
			
		||||
    {0x00450040, nullptr, "GetWirelessRebootInfo"},
 | 
			
		||||
    {0x00460104, nullptr, "Wrap"},
 | 
			
		||||
    {0x00470104, nullptr, "Unwrap"},
 | 
			
		||||
    {0x00460104, Wrap, "Wrap"},
 | 
			
		||||
    {0x00470104, Unwrap, "Unwrap"},
 | 
			
		||||
    {0x00480100, nullptr, "GetProgramInfo"},
 | 
			
		||||
    {0x00490180, nullptr, "Reboot"},
 | 
			
		||||
    {0x004A0040, nullptr, "GetCaptureInfo"},
 | 
			
		||||
 
 | 
			
		||||
@@ -78,8 +78,8 @@ const Interface::FunctionInfo FunctionTable[] = {
 | 
			
		||||
    {0x00430040, NotifyToWait, "NotifyToWait"},
 | 
			
		||||
    {0x00440000, GetSharedFont, "GetSharedFont"},
 | 
			
		||||
    {0x00450040, nullptr, "GetWirelessRebootInfo"},
 | 
			
		||||
    {0x00460104, nullptr, "Wrap"},
 | 
			
		||||
    {0x00470104, nullptr, "Unwrap"},
 | 
			
		||||
    {0x00460104, Wrap, "Wrap"},
 | 
			
		||||
    {0x00470104, Unwrap, "Unwrap"},
 | 
			
		||||
    {0x00480100, nullptr, "GetProgramInfo"},
 | 
			
		||||
    {0x00490180, nullptr, "Reboot"},
 | 
			
		||||
    {0x004A0040, nullptr, "GetCaptureInfo"},
 | 
			
		||||
 
 | 
			
		||||
@@ -78,8 +78,8 @@ const Interface::FunctionInfo FunctionTable[] = {
 | 
			
		||||
    {0x00430040, NotifyToWait, "NotifyToWait"},
 | 
			
		||||
    {0x00440000, GetSharedFont, "GetSharedFont"},
 | 
			
		||||
    {0x00450040, nullptr, "GetWirelessRebootInfo"},
 | 
			
		||||
    {0x00460104, nullptr, "Wrap"},
 | 
			
		||||
    {0x00470104, nullptr, "Unwrap"},
 | 
			
		||||
    {0x00460104, Wrap, "Wrap"},
 | 
			
		||||
    {0x00470104, Unwrap, "Unwrap"},
 | 
			
		||||
    {0x00480100, nullptr, "GetProgramInfo"},
 | 
			
		||||
    {0x00490180, nullptr, "Reboot"},
 | 
			
		||||
    {0x004A0040, nullptr, "GetCaptureInfo"},
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user