summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/citra.cpp3
-rw-r--r--src/citra/config.cpp6
-rw-r--r--src/citra_qt/configuration/config.cpp6
-rw-r--r--src/citra_qt/configuration/configure_debug.ui7
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/function_wrappers.h15
-rw-r--r--src/core/hle/kernel/errors.h5
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp19
-rw-r--r--src/core/hle/kernel/server_port.cpp12
-rw-r--r--src/core/hle/kernel/server_port.h11
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp77
-rw-r--r--src/core/hle/service/nwm/uds_data.cpp278
-rw-r--r--src/core/hle/service/nwm/uds_data.h78
-rw-r--r--src/core/hle/svc.cpp45
-rw-r--r--src/core/hw/aes/key.h2
-rw-r--r--src/tests/core/hle/kernel/hle_ipc.cpp25
16 files changed, 566 insertions, 25 deletions
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index dd357ff72..14574e56c 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -18,7 +18,10 @@
18#endif 18#endif
19 19
20#ifdef _WIN32 20#ifdef _WIN32
21// windows.h needs to be included before shellapi.h
21#include <windows.h> 22#include <windows.h>
23
24#include <shellapi.h>
22#endif 25#endif
23 26
24#include "citra/config.h" 27#include "citra/config.h"
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index f08b4069c..957d8dc86 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -88,9 +88,9 @@ void Config::ReadValues() {
88 Settings::values.toggle_framelimit = 88 Settings::values.toggle_framelimit =
89 sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true); 89 sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true);
90 90
91 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0); 91 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
92 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); 92 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
93 Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0); 93 Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);
94 94
95 // Layout 95 // Layout
96 Settings::values.layout_option = 96 Settings::values.layout_option =
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index 2b99447ec..64ffc9152 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -70,9 +70,9 @@ void Config::ReadValues() {
70 Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); 70 Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool();
71 Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool(); 71 Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool();
72 72
73 Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat(); 73 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
74 Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat(); 74 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
75 Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat(); 75 Settings::values.bg_blue = qt_config->value("bg_blue", 0.0).toFloat();
76 qt_config->endGroup(); 76 qt_config->endGroup();
77 77
78 qt_config->beginGroup("Layout"); 78 qt_config->beginGroup("Layout");
diff --git a/src/citra_qt/configuration/configure_debug.ui b/src/citra_qt/configuration/configure_debug.ui
index bbbb0e3f4..96638ebdb 100644
--- a/src/citra_qt/configuration/configure_debug.ui
+++ b/src/citra_qt/configuration/configure_debug.ui
@@ -23,6 +23,13 @@
23 </property> 23 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_2"> 24 <layout class="QVBoxLayout" name="verticalLayout_2">
25 <item> 25 <item>
26 <widget class="QLabel">
27 <property name="text">
28 <string>The GDB Stub only works correctly when the CPU JIT is off.</string>
29 </property>
30 </widget>
31 </item>
32 <item>
26 <layout class="QHBoxLayout" name="horizontalLayout_3"> 33 <layout class="QHBoxLayout" name="horizontalLayout_3">
27 <item> 34 <item>
28 <widget class="QCheckBox" name="toggle_gdbstub"> 35 <widget class="QCheckBox" name="toggle_gdbstub">
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index b16a89990..ea09819e5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -144,6 +144,7 @@ set(SRCS
144 hle/service/nwm/nwm_tst.cpp 144 hle/service/nwm/nwm_tst.cpp
145 hle/service/nwm/nwm_uds.cpp 145 hle/service/nwm/nwm_uds.cpp
146 hle/service/nwm/uds_beacon.cpp 146 hle/service/nwm/uds_beacon.cpp
147 hle/service/nwm/uds_data.cpp
147 hle/service/pm_app.cpp 148 hle/service/pm_app.cpp
148 hle/service/ptm/ptm.cpp 149 hle/service/ptm/ptm.cpp
149 hle/service/ptm/ptm_gets.cpp 150 hle/service/ptm/ptm_gets.cpp
@@ -341,6 +342,7 @@ set(HEADERS
341 hle/service/nwm/nwm_tst.h 342 hle/service/nwm/nwm_tst.h
342 hle/service/nwm/nwm_uds.h 343 hle/service/nwm/nwm_uds.h
343 hle/service/nwm/uds_beacon.h 344 hle/service/nwm/uds_beacon.h
345 hle/service/nwm/uds_data.h
344 hle/service/pm_app.h 346 hle/service/pm_app.h
345 hle/service/ptm/ptm.h 347 hle/service/ptm/ptm.h
346 hle/service/ptm/ptm_gets.h 348 hle/service/ptm/ptm_gets.h
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 18b6e7017..410bb87ea 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -226,9 +226,18 @@ void Wrap() {
226 u32 retval = func(&param_1, &param_2, 226 u32 retval = func(&param_1, &param_2,
227 reinterpret_cast<const char*>(Memory::GetPointer(PARAM(2))), PARAM(3)) 227 reinterpret_cast<const char*>(Memory::GetPointer(PARAM(2))), PARAM(3))
228 .raw; 228 .raw;
229 // The first out parameter is moved into R2 and the second is moved into R1. 229 Core::CPU().SetReg(1, param_1);
230 Core::CPU().SetReg(1, param_2); 230 Core::CPU().SetReg(2, param_2);
231 Core::CPU().SetReg(2, param_1); 231 FuncReturn(retval);
232}
233
234template <ResultCode func(Kernel::Handle*, Kernel::Handle*)>
235void Wrap() {
236 Kernel::Handle param_1 = 0;
237 Kernel::Handle param_2 = 0;
238 u32 retval = func(&param_1, &param_2).raw;
239 Core::CPU().SetReg(1, param_1);
240 Core::CPU().SetReg(2, param_2);
232 FuncReturn(retval); 241 FuncReturn(retval);
233} 242}
234 243
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index b3b60e7df..64aa61460 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -13,6 +13,7 @@ enum {
13 OutOfHandles = 19, 13 OutOfHandles = 19,
14 SessionClosedByRemote = 26, 14 SessionClosedByRemote = 26,
15 PortNameTooLong = 30, 15 PortNameTooLong = 30,
16 NoPendingSessions = 35,
16 WrongPermission = 46, 17 WrongPermission = 46,
17 InvalidBufferDescriptor = 48, 18 InvalidBufferDescriptor = 48,
18 MaxConnectionsReached = 52, 19 MaxConnectionsReached = 52,
@@ -94,5 +95,9 @@ constexpr ResultCode ERR_OUT_OF_RANGE_KERNEL(ErrorDescription::OutOfRange, Error
94 ErrorLevel::Permanent); // 0xD8E007FD 95 ErrorLevel::Permanent); // 0xD8E007FD
95constexpr ResultCode RESULT_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS, 96constexpr ResultCode RESULT_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS,
96 ErrorSummary::StatusChanged, ErrorLevel::Info); 97 ErrorSummary::StatusChanged, ErrorLevel::Info);
98/// Returned when Accept() is called on a port with no sessions to be accepted.
99constexpr ResultCode ERR_NO_PENDING_SESSIONS(ErrCodes::NoPendingSessions, ErrorModule::OS,
100 ErrorSummary::WouldBlock,
101 ErrorLevel::Permanent); // 0xD8401823
97 102
98} // namespace Kernel 103} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 1cac1d0c9..5ebe2eca4 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -67,10 +67,13 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr
67 ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error 67 ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error
68 for (u32 j = 0; j < num_handles; ++j) { 68 for (u32 j = 0; j < num_handles; ++j) {
69 Handle handle = src_cmdbuf[i]; 69 Handle handle = src_cmdbuf[i];
70 SharedPtr<Object> object = src_table.GetGeneric(handle); 70 SharedPtr<Object> object = nullptr;
71 ASSERT(object != nullptr); // TODO(yuriks): Return error 71 if (handle != 0) {
72 if (descriptor == IPC::DescriptorType::MoveHandle) { 72 object = src_table.GetGeneric(handle);
73 src_table.Close(handle); 73 ASSERT(object != nullptr); // TODO(yuriks): Return error
74 if (descriptor == IPC::DescriptorType::MoveHandle) {
75 src_table.Close(handle);
76 }
74 } 77 }
75 78
76 cmd_buf[i++] = AddOutgoingHandle(std::move(object)); 79 cmd_buf[i++] = AddOutgoingHandle(std::move(object));
@@ -112,9 +115,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
112 ASSERT(i + num_handles <= command_size); 115 ASSERT(i + num_handles <= command_size);
113 for (u32 j = 0; j < num_handles; ++j) { 116 for (u32 j = 0; j < num_handles; ++j) {
114 SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]); 117 SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]);
115 118 Handle handle = 0;
116 // TODO(yuriks): Figure out the proper error handling for if this fails 119 if (object != nullptr) {
117 Handle handle = dst_table.Create(object).Unwrap(); 120 // TODO(yuriks): Figure out the proper error handling for if this fails
121 handle = dst_table.Create(object).Unwrap();
122 }
118 dst_cmdbuf[i++] = handle; 123 dst_cmdbuf[i++] = handle;
119 } 124 }
120 break; 125 break;
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index 4d20c39a1..49a9cdfa3 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -5,8 +5,10 @@
5#include <tuple> 5#include <tuple>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/hle/kernel/client_port.h" 7#include "core/hle/kernel/client_port.h"
8#include "core/hle/kernel/errors.h"
8#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/server_port.h" 10#include "core/hle/kernel/server_port.h"
11#include "core/hle/kernel/server_session.h"
10#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
11 13
12namespace Kernel { 14namespace Kernel {
@@ -14,6 +16,16 @@ namespace Kernel {
14ServerPort::ServerPort() {} 16ServerPort::ServerPort() {}
15ServerPort::~ServerPort() {} 17ServerPort::~ServerPort() {}
16 18
19ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
20 if (pending_sessions.empty()) {
21 return ERR_NO_PENDING_SESSIONS;
22 }
23
24 auto session = std::move(pending_sessions.back());
25 pending_sessions.pop_back();
26 return MakeResult(std::move(session));
27}
28
17bool ServerPort::ShouldWait(Thread* thread) const { 29bool ServerPort::ShouldWait(Thread* thread) const {
18 // If there are no pending sessions, we wait until a new one is added. 30 // If there are no pending sessions, we wait until a new one is added.
19 return pending_sessions.size() == 0; 31 return pending_sessions.size() == 0;
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index f1419cd46..6fe7c7f2f 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -14,6 +14,7 @@
14namespace Kernel { 14namespace Kernel {
15 15
16class ClientPort; 16class ClientPort;
17class ServerSession;
17class SessionRequestHandler; 18class SessionRequestHandler;
18 19
19class ServerPort final : public WaitObject { 20class ServerPort final : public WaitObject {
@@ -41,6 +42,12 @@ public:
41 } 42 }
42 43
43 /** 44 /**
45 * Accepts a pending incoming connection on this port. If there are no pending sessions, will
46 * return ERR_NO_PENDING_SESSIONS.
47 */
48 ResultVal<SharedPtr<ServerSession>> Accept();
49
50 /**
44 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port 51 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
45 * will inherit a reference to this handler. 52 * will inherit a reference to this handler.
46 */ 53 */
@@ -50,8 +57,8 @@ public:
50 57
51 std::string name; ///< Name of port (optional) 58 std::string name; ///< Name of port (optional)
52 59
53 std::vector<SharedPtr<WaitObject>> 60 /// ServerSessions waiting to be accepted by the port
54 pending_sessions; ///< ServerSessions waiting to be accepted by the port 61 std::vector<SharedPtr<ServerSession>> pending_sessions;
55 62
56 /// This session's HLE request handler template (optional) 63 /// This session's HLE request handler template (optional)
57 /// ServerSessions created from this port inherit a reference to this handler. 64 /// ServerSessions created from this port inherit a reference to this handler.
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
index a7149c9e8..6dbdff044 100644
--- a/src/core/hle/service/nwm/nwm_uds.cpp
+++ b/src/core/hle/service/nwm/nwm_uds.cpp
@@ -15,6 +15,7 @@
15#include "core/hle/result.h" 15#include "core/hle/result.h"
16#include "core/hle/service/nwm/nwm_uds.h" 16#include "core/hle/service/nwm/nwm_uds.h"
17#include "core/hle/service/nwm/uds_beacon.h" 17#include "core/hle/service/nwm/uds_beacon.h"
18#include "core/hle/service/nwm/uds_data.h"
18#include "core/memory.h" 19#include "core/memory.h"
19 20
20namespace Service { 21namespace Service {
@@ -373,6 +374,80 @@ static void DestroyNetwork(Interface* self) {
373} 374}
374 375
375/** 376/**
377 * NWM_UDS::SendTo service function.
378 * Sends a data frame to the UDS network we're connected to.
379 * Inputs:
380 * 0 : Command header.
381 * 1 : Unknown.
382 * 2 : u16 Destination network node id.
383 * 3 : u8 Data channel.
384 * 4 : Buffer size >> 2
385 * 5 : Data size
386 * 6 : Flags
387 * 7 : Input buffer descriptor
388 * 8 : Input buffer address
389 * Outputs:
390 * 0 : Return header
391 * 1 : Result of function, 0 on success, otherwise error code
392 */
393static void SendTo(Interface* self) {
394 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x17, 6, 2);
395
396 rp.Skip(1, false);
397 u16 dest_node_id = rp.Pop<u16>();
398 u8 data_channel = rp.Pop<u8>();
399 rp.Skip(1, false);
400 u32 data_size = rp.Pop<u32>();
401 u32 flags = rp.Pop<u32>();
402
403 size_t desc_size;
404 const VAddr input_address = rp.PopStaticBuffer(&desc_size, false);
405 ASSERT(desc_size == data_size);
406
407 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
408
409 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) &&
410 connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
411 rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
412 ErrorSummary::InvalidState, ErrorLevel::Status));
413 return;
414 }
415
416 if (dest_node_id == connection_status.network_node_id) {
417 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS,
418 ErrorSummary::WrongArgument, ErrorLevel::Status));
419 return;
420 }
421
422 // TODO(Subv): Do something with the flags.
423
424 constexpr size_t MaxSize = 0x5C6;
425 if (data_size > MaxSize) {
426 rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS,
427 ErrorSummary::WrongArgument, ErrorLevel::Usage));
428 return;
429 }
430
431 std::vector<u8> data(data_size);
432 Memory::ReadBlock(input_address, data.data(), data.size());
433
434 // TODO(Subv): Increment the sequence number after each sent packet.
435 u16 sequence_number = 0;
436 std::vector<u8> data_payload = GenerateDataPayload(
437 data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number);
438
439 // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt
440 // and encapsulate the payload.
441
442 // TODO(Subv): Send the frame.
443
444 rb.Push(RESULT_SUCCESS);
445
446 LOG_WARNING(Service_NWM, "(STUB) called dest_node_id=%u size=%u flags=%u channel=%u",
447 static_cast<u32>(dest_node_id), data_size, flags, static_cast<u32>(data_channel));
448}
449
450/**
376 * NWM_UDS::GetChannel service function. 451 * NWM_UDS::GetChannel service function.
377 * Returns the WiFi channel in which the network we're connected to is transmitting. 452 * Returns the WiFi channel in which the network we're connected to is transmitting.
378 * Inputs: 453 * Inputs:
@@ -600,7 +675,7 @@ const Interface::FunctionInfo FunctionTable[] = {
600 {0x00130040, nullptr, "Unbind"}, 675 {0x00130040, nullptr, "Unbind"},
601 {0x001400C0, nullptr, "PullPacket"}, 676 {0x001400C0, nullptr, "PullPacket"},
602 {0x00150080, nullptr, "SetMaxSendDelay"}, 677 {0x00150080, nullptr, "SetMaxSendDelay"},
603 {0x00170182, nullptr, "SendTo"}, 678 {0x00170182, SendTo, "SendTo"},
604 {0x001A0000, GetChannel, "GetChannel"}, 679 {0x001A0000, GetChannel, "GetChannel"},
605 {0x001B0302, InitializeWithVersion, "InitializeWithVersion"}, 680 {0x001B0302, InitializeWithVersion, "InitializeWithVersion"},
606 {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"}, 681 {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"},
diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp
new file mode 100644
index 000000000..8c6742dba
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_data.cpp
@@ -0,0 +1,278 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include <cryptopp/aes.h>
7#include <cryptopp/ccm.h>
8#include <cryptopp/filters.h>
9#include <cryptopp/md5.h>
10#include <cryptopp/modes.h>
11#include "core/hle/service/nwm/nwm_uds.h"
12#include "core/hle/service/nwm/uds_data.h"
13#include "core/hw/aes/key.h"
14
15namespace Service {
16namespace NWM {
17
18using MacAddress = std::array<u8, 6>;
19
20/*
21 * Generates a SNAP-enabled 802.2 LLC header for the specified protocol.
22 * @returns a buffer with the bytes of the generated header.
23 */
24static std::vector<u8> GenerateLLCHeader(EtherType protocol) {
25 LLCHeader header{};
26 header.protocol = static_cast<u16>(protocol);
27
28 std::vector<u8> buffer(sizeof(header));
29 memcpy(buffer.data(), &header, sizeof(header));
30
31 return buffer;
32}
33
34/*
35 * Generates a Nintendo UDS SecureData header with the specified parameters.
36 * @returns a buffer with the bytes of the generated header.
37 */
38static std::vector<u8> GenerateSecureDataHeader(u16 data_size, u8 channel, u16 dest_node_id,
39 u16 src_node_id, u16 sequence_number) {
40 SecureDataHeader header{};
41 header.protocol_size = data_size + sizeof(SecureDataHeader);
42 // Note: This size includes everything except the first 4 bytes of the structure,
43 // reinforcing the hypotheses that the first 4 bytes are actually the header of
44 // another container protocol.
45 header.securedata_size = data_size + sizeof(SecureDataHeader) - 4;
46 // Frames sent by the emulated application are never UDS management frames
47 header.is_management = 0;
48 header.data_channel = channel;
49 header.sequence_number = sequence_number;
50 header.dest_node_id = dest_node_id;
51 header.src_node_id = src_node_id;
52
53 std::vector<u8> buffer(sizeof(header));
54 memcpy(buffer.data(), &header, sizeof(header));
55
56 return buffer;
57}
58
59/*
60 * Calculates the CTR used for the AES-CTR process that calculates
61 * the CCMP crypto key for data frames.
62 * @returns The CTR used for data frames crypto key generation.
63 */
64static std::array<u8, CryptoPP::MD5::DIGESTSIZE> GetDataCryptoCTR(const NetworkInfo& network_info) {
65 DataFrameCryptoCTR data{};
66
67 data.host_mac = network_info.host_mac_address;
68 data.wlan_comm_id = network_info.wlan_comm_id;
69 data.id = network_info.id;
70 data.network_id = network_info.network_id;
71
72 std::array<u8, CryptoPP::MD5::DIGESTSIZE> hash;
73 CryptoPP::MD5().CalculateDigest(hash.data(), reinterpret_cast<u8*>(&data), sizeof(data));
74
75 return hash;
76}
77
78/*
79 * Generates the key used for encrypting the 802.11 data frames generated by UDS.
80 * @returns The key used for data frames crypto.
81 */
82static std::array<u8, CryptoPP::AES::BLOCKSIZE> GenerateDataCCMPKey(
83 const std::vector<u8>& passphrase, const NetworkInfo& network_info) {
84 // Calculate the MD5 hash of the input passphrase.
85 std::array<u8, CryptoPP::MD5::DIGESTSIZE> passphrase_hash;
86 CryptoPP::MD5().CalculateDigest(passphrase_hash.data(), passphrase.data(), passphrase.size());
87
88 std::array<u8, CryptoPP::AES::BLOCKSIZE> ccmp_key;
89
90 // The CCMP key is the result of encrypting the MD5 hash of the passphrase with AES-CTR using
91 // keyslot 0x2D.
92 using CryptoPP::AES;
93 std::array<u8, CryptoPP::MD5::DIGESTSIZE> counter = GetDataCryptoCTR(network_info);
94 std::array<u8, AES::BLOCKSIZE> key = HW::AES::GetNormalKey(HW::AES::KeySlotID::UDSDataKey);
95 CryptoPP::CTR_Mode<AES>::Encryption aes;
96 aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, counter.data());
97 aes.ProcessData(ccmp_key.data(), passphrase_hash.data(), passphrase_hash.size());
98
99 return ccmp_key;
100}
101
102/*
103 * Generates the Additional Authenticated Data (AAD) for an UDS 802.11 encrypted data frame.
104 * @returns a buffer with the bytes of the AAD.
105 */
106static std::vector<u8> GenerateCCMPAAD(const MacAddress& sender, const MacAddress& receiver,
107 const MacAddress& bssid, u16 frame_control) {
108 // Reference: IEEE 802.11-2007
109
110 // 8.3.3.3.2 Construct AAD (22-30 bytes)
111 // The AAD is constructed from the MPDU header. The AAD does not include the header Duration
112 // field, because the Duration field value can change due to normal IEEE 802.11 operation (e.g.,
113 // a rate change during retransmission). For similar reasons, several subfields in the Frame
114 // Control field are masked to 0.
115 struct {
116 u16_be FC; // MPDU Frame Control field
117 MacAddress A1;
118 MacAddress A2;
119 MacAddress A3;
120 u16_be SC; // MPDU Sequence Control field
121 } aad_struct{};
122
123 constexpr u16 AADFrameControlMask = 0x8FC7;
124 aad_struct.FC = frame_control & AADFrameControlMask;
125 aad_struct.SC = 0;
126
127 bool to_ds = (frame_control & (1 << 0)) != 0;
128 bool from_ds = (frame_control & (1 << 1)) != 0;
129 // In the 802.11 standard, ToDS = 1 and FromDS = 1 is a valid configuration,
130 // however, the 3DS doesn't seem to transmit frames with such combination.
131 ASSERT_MSG(to_ds != from_ds, "Invalid combination");
132
133 // The meaning of the address fields depends on the ToDS and FromDS fields.
134 if (from_ds) {
135 aad_struct.A1 = receiver;
136 aad_struct.A2 = bssid;
137 aad_struct.A3 = sender;
138 }
139
140 if (to_ds) {
141 aad_struct.A1 = bssid;
142 aad_struct.A2 = sender;
143 aad_struct.A3 = receiver;
144 }
145
146 std::vector<u8> aad(sizeof(aad_struct));
147 std::memcpy(aad.data(), &aad_struct, sizeof(aad_struct));
148
149 return aad;
150}
151
152/*
153 * Decrypts the payload of an encrypted 802.11 data frame using the specified key.
154 * @returns The decrypted payload.
155 */
156static std::vector<u8> DecryptDataFrame(const std::vector<u8>& encrypted_payload,
157 const std::array<u8, CryptoPP::AES::BLOCKSIZE>& ccmp_key,
158 const MacAddress& sender, const MacAddress& receiver,
159 const MacAddress& bssid, u16 sequence_number,
160 u16 frame_control) {
161
162 // Reference: IEEE 802.11-2007
163
164 std::vector<u8> aad = GenerateCCMPAAD(sender, receiver, bssid, frame_control);
165
166 std::vector<u8> packet_number{0,
167 0,
168 0,
169 0,
170 static_cast<u8>((sequence_number >> 8) & 0xFF),
171 static_cast<u8>(sequence_number & 0xFF)};
172
173 // 8.3.3.3.3 Construct CCM nonce (13 bytes)
174 std::vector<u8> nonce;
175 nonce.push_back(0); // priority
176 nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2
177 nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN
178
179 try {
180 CryptoPP::CCM<CryptoPP::AES, 8>::Decryption d;
181 d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size());
182 d.SpecifyDataLengths(aad.size(), encrypted_payload.size() - 8, 0);
183
184 CryptoPP::AuthenticatedDecryptionFilter df(
185 d, nullptr, CryptoPP::AuthenticatedDecryptionFilter::MAC_AT_END |
186 CryptoPP::AuthenticatedDecryptionFilter::THROW_EXCEPTION);
187 // put aad
188 df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size());
189
190 // put cipher with mac
191 df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, encrypted_payload.data(),
192 encrypted_payload.size() - 8);
193 df.ChannelPut(CryptoPP::DEFAULT_CHANNEL,
194 encrypted_payload.data() + encrypted_payload.size() - 8, 8);
195
196 df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL);
197 df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL);
198 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL);
199
200 int size = df.MaxRetrievable();
201
202 std::vector<u8> pdata(size);
203 df.Get(pdata.data(), size);
204 return pdata;
205 } catch (CryptoPP::Exception&) {
206 LOG_ERROR(Service_NWM, "failed to decrypt");
207 }
208
209 return {};
210}
211
212/*
213 * Encrypts the payload of an 802.11 data frame using the specified key.
214 * @returns The encrypted payload.
215 */
216static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload,
217 const std::array<u8, CryptoPP::AES::BLOCKSIZE>& ccmp_key,
218 const MacAddress& sender, const MacAddress& receiver,
219 const MacAddress& bssid, u16 sequence_number,
220 u16 frame_control) {
221 // Reference: IEEE 802.11-2007
222
223 std::vector<u8> aad = GenerateCCMPAAD(sender, receiver, bssid, frame_control);
224
225 std::vector<u8> packet_number{0,
226 0,
227 0,
228 0,
229 static_cast<u8>((sequence_number >> 8) & 0xFF),
230 static_cast<u8>(sequence_number & 0xFF)};
231
232 // 8.3.3.3.3 Construct CCM nonce (13 bytes)
233 std::vector<u8> nonce;
234 nonce.push_back(0); // priority
235 nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2
236 nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN
237
238 try {
239 CryptoPP::CCM<CryptoPP::AES, 8>::Encryption d;
240 d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size());
241 d.SpecifyDataLengths(aad.size(), payload.size(), 0);
242
243 CryptoPP::AuthenticatedEncryptionFilter df(d);
244 // put aad
245 df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size());
246 df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL);
247
248 // put plaintext
249 df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, payload.data(), payload.size());
250 df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL);
251
252 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL);
253
254 int size = df.MaxRetrievable();
255
256 std::vector<u8> cipher(size);
257 df.Get(cipher.data(), size);
258 return cipher;
259 } catch (CryptoPP::Exception&) {
260 LOG_ERROR(Service_NWM, "failed to encrypt");
261 }
262
263 return {};
264}
265
266std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
267 u16 src_node, u16 sequence_number) {
268 std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData);
269 std::vector<u8> securedata_header =
270 GenerateSecureDataHeader(data.size(), channel, dest_node, src_node, sequence_number);
271
272 buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end());
273 buffer.insert(buffer.end(), data.begin(), data.end());
274 return buffer;
275}
276
277} // namespace NWM
278} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h
new file mode 100644
index 000000000..a23520a41
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_data.h
@@ -0,0 +1,78 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <vector>
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/service.h"
12
13namespace Service {
14namespace NWM {
15
16enum class SAP : u8 { SNAPExtensionUsed = 0xAA };
17
18enum class PDUControl : u8 { UnnumberedInformation = 3 };
19
20enum class EtherType : u16 { SecureData = 0x876D, EAPoL = 0x888E };
21
22/*
23 * 802.2 header, UDS packets always use SNAP for these headers,
24 * which means the dsap and ssap are always SNAPExtensionUsed (0xAA)
25 * and the OUI is always 0.
26 */
27struct LLCHeader {
28 u8 dsap = static_cast<u8>(SAP::SNAPExtensionUsed);
29 u8 ssap = static_cast<u8>(SAP::SNAPExtensionUsed);
30 u8 control = static_cast<u8>(PDUControl::UnnumberedInformation);
31 std::array<u8, 3> OUI = {};
32 u16_be protocol;
33};
34
35static_assert(sizeof(LLCHeader) == 8, "LLCHeader has the wrong size");
36
37/*
38 * Nintendo SecureData header, every UDS packet contains one,
39 * it is used to store metadata about the transmission such as
40 * the source and destination network node ids.
41 */
42struct SecureDataHeader {
43 // TODO(Subv): It is likely that the first 4 bytes of this header are
44 // actually part of another container protocol.
45 u16_be protocol_size;
46 INSERT_PADDING_BYTES(2);
47 u16_be securedata_size;
48 u8 is_management;
49 u8 data_channel;
50 u16_be sequence_number;
51 u16_be dest_node_id;
52 u16_be src_node_id;
53};
54
55static_assert(sizeof(SecureDataHeader) == 14, "SecureDataHeader has the wrong size");
56
57/*
58 * The raw bytes of this structure are the CTR used in the encryption (AES-CTR)
59 * process used to generate the CCMP key for data frame encryption.
60 */
61struct DataFrameCryptoCTR {
62 u32_le wlan_comm_id;
63 u32_le network_id;
64 std::array<u8, 6> host_mac;
65 u16_le id;
66};
67
68static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size");
69
70/**
71 * Generates an unencrypted 802.11 data payload.
72 * @returns The generated frame payload.
73 */
74std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
75 u16 src_node, u16 sequence_number);
76
77} // namespace NWM
78} // namespace Service
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index e68b9f16a..c05401143 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -36,8 +36,9 @@
36//////////////////////////////////////////////////////////////////////////////////////////////////// 36////////////////////////////////////////////////////////////////////////////////////////////////////
37// Namespace SVC 37// Namespace SVC
38 38
39using Kernel::SharedPtr;
40using Kernel::ERR_INVALID_HANDLE; 39using Kernel::ERR_INVALID_HANDLE;
40using Kernel::Handle;
41using Kernel::SharedPtr;
41 42
42namespace SVC { 43namespace SVC {
43 44
@@ -933,7 +934,6 @@ static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client
933 934
934 using Kernel::ServerPort; 935 using Kernel::ServerPort;
935 using Kernel::ClientPort; 936 using Kernel::ClientPort;
936 using Kernel::SharedPtr;
937 937
938 auto ports = ServerPort::CreatePortPair(max_sessions); 938 auto ports = ServerPort::CreatePortPair(max_sessions);
939 CASCADE_RESULT(*client_port, Kernel::g_handle_table.Create( 939 CASCADE_RESULT(*client_port, Kernel::g_handle_table.Create(
@@ -947,6 +947,41 @@ static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client
947 return RESULT_SUCCESS; 947 return RESULT_SUCCESS;
948} 948}
949 949
950static ResultCode CreateSessionToPort(Handle* out_client_session, Handle client_port_handle) {
951 using Kernel::ClientPort;
952 SharedPtr<ClientPort> client_port = Kernel::g_handle_table.Get<ClientPort>(client_port_handle);
953 if (client_port == nullptr)
954 return ERR_INVALID_HANDLE;
955
956 CASCADE_RESULT(auto session, client_port->Connect());
957 CASCADE_RESULT(*out_client_session, Kernel::g_handle_table.Create(std::move(session)));
958 return RESULT_SUCCESS;
959}
960
961static ResultCode CreateSession(Handle* server_session, Handle* client_session) {
962 auto sessions = Kernel::ServerSession::CreateSessionPair();
963
964 auto& server = std::get<SharedPtr<Kernel::ServerSession>>(sessions);
965 CASCADE_RESULT(*server_session, Kernel::g_handle_table.Create(std::move(server)));
966
967 auto& client = std::get<SharedPtr<Kernel::ClientSession>>(sessions);
968 CASCADE_RESULT(*client_session, Kernel::g_handle_table.Create(std::move(client)));
969
970 LOG_TRACE(Kernel_SVC, "called");
971 return RESULT_SUCCESS;
972}
973
974static ResultCode AcceptSession(Handle* out_server_session, Handle server_port_handle) {
975 using Kernel::ServerPort;
976 SharedPtr<ServerPort> server_port = Kernel::g_handle_table.Get<ServerPort>(server_port_handle);
977 if (server_port == nullptr)
978 return ERR_INVALID_HANDLE;
979
980 CASCADE_RESULT(auto session, server_port->Accept());
981 CASCADE_RESULT(*out_server_session, Kernel::g_handle_table.Create(std::move(session)));
982 return RESULT_SUCCESS;
983}
984
950static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) { 985static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) {
951 using Kernel::MemoryRegion; 986 using Kernel::MemoryRegion;
952 987
@@ -1121,9 +1156,9 @@ static const FunctionDef SVC_Table[] = {
1121 {0x45, nullptr, "Unknown"}, 1156 {0x45, nullptr, "Unknown"},
1122 {0x46, nullptr, "Unknown"}, 1157 {0x46, nullptr, "Unknown"},
1123 {0x47, HLE::Wrap<CreatePort>, "CreatePort"}, 1158 {0x47, HLE::Wrap<CreatePort>, "CreatePort"},
1124 {0x48, nullptr, "CreateSessionToPort"}, 1159 {0x48, HLE::Wrap<CreateSessionToPort>, "CreateSessionToPort"},
1125 {0x49, nullptr, "CreateSession"}, 1160 {0x49, HLE::Wrap<CreateSession>, "CreateSession"},
1126 {0x4A, nullptr, "AcceptSession"}, 1161 {0x4A, HLE::Wrap<AcceptSession>, "AcceptSession"},
1127 {0x4B, nullptr, "ReplyAndReceive1"}, 1162 {0x4B, nullptr, "ReplyAndReceive1"},
1128 {0x4C, nullptr, "ReplyAndReceive2"}, 1163 {0x4C, nullptr, "ReplyAndReceive2"},
1129 {0x4D, nullptr, "ReplyAndReceive3"}, 1164 {0x4D, nullptr, "ReplyAndReceive3"},
diff --git a/src/core/hw/aes/key.h b/src/core/hw/aes/key.h
index b01d04f13..c9f1342f4 100644
--- a/src/core/hw/aes/key.h
+++ b/src/core/hw/aes/key.h
@@ -12,6 +12,8 @@ namespace HW {
12namespace AES { 12namespace AES {
13 13
14enum KeySlotID : size_t { 14enum KeySlotID : size_t {
15 // AES Keyslot used to generate the UDS data frame CCMP key.
16 UDSDataKey = 0x2D,
15 APTWrap = 0x31, 17 APTWrap = 0x31,
16 18
17 MaxKeySlotID = 0x40, 19 MaxKeySlotID = 0x40,
diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp
index e07a28c5b..52336d027 100644
--- a/src/tests/core/hle/kernel/hle_ipc.cpp
+++ b/src/tests/core/hle/kernel/hle_ipc.cpp
@@ -18,7 +18,7 @@ static SharedPtr<Object> MakeObject() {
18 return Event::Create(ResetType::OneShot); 18 return Event::Create(ResetType::OneShot);
19} 19}
20 20
21TEST_CASE("HLERequestContext::PopoulateFromIncomingCommandBuffer", "[core][kernel]") { 21TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") {
22 auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); 22 auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair());
23 HLERequestContext context(std::move(session)); 23 HLERequestContext context(std::move(session));
24 24
@@ -94,6 +94,18 @@ TEST_CASE("HLERequestContext::PopoulateFromIncomingCommandBuffer", "[core][kerne
94 REQUIRE(context.GetIncomingHandle(output[5]) == c); 94 REQUIRE(context.GetIncomingHandle(output[5]) == c);
95 } 95 }
96 96
97 SECTION("translates null handles") {
98 const u32_le input[]{
99 IPC::MakeHeader(0, 0, 2), IPC::MoveHandleDesc(1), 0,
100 };
101
102 auto result = context.PopulateFromIncomingCommandBuffer(input, *process, handle_table);
103
104 REQUIRE(result == RESULT_SUCCESS);
105 auto* output = context.CommandBuffer();
106 REQUIRE(context.GetIncomingHandle(output[2]) == nullptr);
107 }
108
97 SECTION("translates CallingPid descriptors") { 109 SECTION("translates CallingPid descriptors") {
98 const u32_le input[]{ 110 const u32_le input[]{
99 IPC::MakeHeader(0, 0, 2), IPC::CallingPidDesc(), 0x98989898, 111 IPC::MakeHeader(0, 0, 2), IPC::CallingPidDesc(), 0x98989898,
@@ -171,6 +183,17 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
171 REQUIRE(handle_table.GetGeneric(output[4]) == b); 183 REQUIRE(handle_table.GetGeneric(output[4]) == b);
172 } 184 }
173 185
186 SECTION("translates null handles") {
187 input[0] = IPC::MakeHeader(0, 0, 2);
188 input[1] = IPC::MoveHandleDesc(1);
189 input[2] = context.AddOutgoingHandle(nullptr);
190
191 auto result = context.WriteToOutgoingCommandBuffer(output, *process, handle_table);
192
193 REQUIRE(result == RESULT_SUCCESS);
194 REQUIRE(output[2] == 0);
195 }
196
174 SECTION("translates multi-handle descriptors") { 197 SECTION("translates multi-handle descriptors") {
175 auto a = MakeObject(); 198 auto a = MakeObject();
176 auto b = MakeObject(); 199 auto b = MakeObject();