summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp122
-rw-r--r--src/core/hle/service/nwm/nwm_uds.h5
-rw-r--r--src/core/hle/service/nwm/uds_beacon.cpp333
-rw-r--r--src/core/hle/service/nwm/uds_beacon.h173
5 files changed, 602 insertions, 33 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index e404063f0..b894564b6 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -139,6 +139,7 @@ set(SRCS
139 hle/service/nwm/nwm_soc.cpp 139 hle/service/nwm/nwm_soc.cpp
140 hle/service/nwm/nwm_tst.cpp 140 hle/service/nwm/nwm_tst.cpp
141 hle/service/nwm/nwm_uds.cpp 141 hle/service/nwm/nwm_uds.cpp
142 hle/service/nwm/uds_beacon.cpp
142 hle/service/pm_app.cpp 143 hle/service/pm_app.cpp
143 hle/service/ptm/ptm.cpp 144 hle/service/ptm/ptm.cpp
144 hle/service/ptm/ptm_gets.cpp 145 hle/service/ptm/ptm_gets.cpp
@@ -326,6 +327,7 @@ set(HEADERS
326 hle/service/nwm/nwm_soc.h 327 hle/service/nwm/nwm_soc.h
327 hle/service/nwm/nwm_tst.h 328 hle/service/nwm/nwm_tst.h
328 hle/service/nwm/nwm_uds.h 329 hle/service/nwm/nwm_uds.h
330 hle/service/nwm/uds_beacon.h
329 hle/service/pm_app.h 331 hle/service/pm_app.h
330 hle/service/ptm/ptm.h 332 hle/service/ptm/ptm.h
331 hle/service/ptm/ptm_gets.h 333 hle/service/ptm/ptm_gets.h
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
index ef6c5ebe3..581816e81 100644
--- a/src/core/hle/service/nwm/nwm_uds.cpp
+++ b/src/core/hle/service/nwm/nwm_uds.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
5#include <cstring> 6#include <cstring>
6#include <unordered_map> 7#include <unordered_map>
7#include <vector> 8#include <vector>
@@ -12,6 +13,7 @@
12#include "core/hle/kernel/shared_memory.h" 13#include "core/hle/kernel/shared_memory.h"
13#include "core/hle/result.h" 14#include "core/hle/result.h"
14#include "core/hle/service/nwm/nwm_uds.h" 15#include "core/hle/service/nwm/nwm_uds.h"
16#include "core/hle/service/nwm/uds_beacon.h"
15#include "core/memory.h" 17#include "core/memory.h"
16 18
17namespace Service { 19namespace Service {
@@ -27,10 +29,12 @@ static Kernel::SharedPtr<Kernel::SharedMemory> recv_buffer_memory;
27// Connection status of this 3DS. 29// Connection status of this 3DS.
28static ConnectionStatus connection_status{}; 30static ConnectionStatus connection_status{};
29 31
30// Node information about the current 3DS. 32/* Node information about the current network.
31// TODO(Subv): Keep an array of all nodes connected to the network, 33 * The amount of elements in this vector is always the maximum number
32// that data has to be retransmitted in every beacon frame. 34 * of nodes specified in the network configuration.
33static NodeInfo node_info; 35 * The first node is always the host, so this always contains at least 1 entry.
36 */
37static NodeList node_info(1);
34 38
35// Mapping of bind node ids to their respective events. 39// Mapping of bind node ids to their respective events.
36static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; 40static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events;
@@ -82,29 +86,70 @@ static void Shutdown(Interface* self) {
82 * 1 : Result of function, 0 on success, otherwise error code 86 * 1 : Result of function, 0 on success, otherwise error code
83 */ 87 */
84static void RecvBeaconBroadcastData(Interface* self) { 88static void RecvBeaconBroadcastData(Interface* self) {
85 u32* cmd_buff = Kernel::GetCommandBuffer(); 89 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0F, 16, 4);
86 u32 out_buffer_size = cmd_buff[1];
87 u32 unk1 = cmd_buff[2];
88 u32 unk2 = cmd_buff[3];
89 u32 mac_address = cmd_buff[4];
90 90
91 u32 unk3 = cmd_buff[6]; 91 u32 out_buffer_size = rp.Pop<u32>();
92 u32 unk1 = rp.Pop<u32>();
93 u32 unk2 = rp.Pop<u32>();
92 94
93 u32 wlan_comm_id = cmd_buff[15]; 95 MacAddress mac_address;
94 u32 ctr_gen_id = cmd_buff[16]; 96 rp.PopRaw(mac_address);
95 u32 value = cmd_buff[17];
96 u32 input_handle = cmd_buff[18];
97 u32 new_buffer_size = cmd_buff[19];
98 u32 out_buffer_ptr = cmd_buff[20];
99 97
100 cmd_buff[1] = RESULT_SUCCESS.raw; 98 rp.Skip(9, false);
101 99
102 LOG_WARNING(Service_NWM, 100 u32 wlan_comm_id = rp.Pop<u32>();
103 "(STUBBED) called out_buffer_size=0x%08X, unk1=0x%08X, unk2=0x%08X," 101 u32 id = rp.Pop<u32>();
104 "mac_address=0x%08X, unk3=0x%08X, wlan_comm_id=0x%08X, ctr_gen_id=0x%08X," 102 Kernel::Handle input_handle = rp.PopHandle();
105 "value=%u, input_handle=0x%08X, new_buffer_size=0x%08X, out_buffer_ptr=0x%08X", 103
106 out_buffer_size, unk1, unk2, mac_address, unk3, wlan_comm_id, ctr_gen_id, value, 104 size_t desc_size;
107 input_handle, new_buffer_size, out_buffer_ptr); 105 const VAddr out_buffer_ptr = rp.PopMappedBuffer(&desc_size);
106 ASSERT(desc_size == out_buffer_size);
107
108 VAddr current_buffer_pos = out_buffer_ptr;
109 u32 total_size = sizeof(BeaconDataReplyHeader);
110
111 // Retrieve all beacon frames that were received from the desired mac address.
112 std::deque<WifiPacket> beacons =
113 GetReceivedPackets(WifiPacket::PacketType::Beacon, mac_address);
114
115 BeaconDataReplyHeader data_reply_header{};
116 data_reply_header.total_entries = beacons.size();
117 data_reply_header.max_output_size = out_buffer_size;
118
119 Memory::WriteBlock(current_buffer_pos, &data_reply_header, sizeof(BeaconDataReplyHeader));
120 current_buffer_pos += sizeof(BeaconDataReplyHeader);
121
122 // Write each of the received beacons into the buffer
123 for (const auto& beacon : beacons) {
124 BeaconEntryHeader entry{};
125 // TODO(Subv): Figure out what this size is used for.
126 entry.unk_size = sizeof(BeaconEntryHeader) + beacon.data.size();
127 entry.total_size = sizeof(BeaconEntryHeader) + beacon.data.size();
128 entry.wifi_channel = beacon.channel;
129 entry.header_size = sizeof(BeaconEntryHeader);
130 entry.mac_address = beacon.transmitter_address;
131
132 ASSERT(current_buffer_pos < out_buffer_ptr + out_buffer_size);
133
134 Memory::WriteBlock(current_buffer_pos, &entry, sizeof(BeaconEntryHeader));
135 current_buffer_pos += sizeof(BeaconEntryHeader);
136
137 Memory::WriteBlock(current_buffer_pos, beacon.data.data(), beacon.data.size());
138 current_buffer_pos += beacon.data.size();
139
140 total_size += sizeof(BeaconEntryHeader) + beacon.data.size();
141 }
142
143 // Update the total size in the structure and write it to the buffer again.
144 data_reply_header.total_size = total_size;
145 Memory::WriteBlock(out_buffer_ptr, &data_reply_header, sizeof(BeaconDataReplyHeader));
146
147 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
148 rb.Push(RESULT_SUCCESS);
149
150 LOG_DEBUG(Service_NWM, "called out_buffer_size=0x%08X, wlan_comm_id=0x%08X, id=0x%08X,"
151 "input_handle=0x%08X, out_buffer_ptr=0x%08X, unk1=0x%08X, unk2=0x%08X",
152 out_buffer_size, wlan_comm_id, id, input_handle, out_buffer_ptr, unk1, unk2);
108} 153}
109 154
110/** 155/**
@@ -127,10 +172,10 @@ static void InitializeWithVersion(Interface* self) {
127 u32 sharedmem_size = rp.Pop<u32>(); 172 u32 sharedmem_size = rp.Pop<u32>();
128 173
129 // Update the node information with the data the game gave us. 174 // Update the node information with the data the game gave us.
130 rp.PopRaw(node_info); 175 rp.PopRaw(node_info[0]);
176
177 u16 version = rp.Pop<u16>();
131 178
132 u16 version;
133 rp.PopRaw(version);
134 Kernel::Handle sharedmem_handle = rp.PopHandle(); 179 Kernel::Handle sharedmem_handle = rp.PopHandle();
135 180
136 recv_buffer_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(sharedmem_handle); 181 recv_buffer_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(sharedmem_handle);
@@ -191,10 +236,8 @@ static void Bind(Interface* self) {
191 236
192 u32 bind_node_id = rp.Pop<u32>(); 237 u32 bind_node_id = rp.Pop<u32>();
193 u32 recv_buffer_size = rp.Pop<u32>(); 238 u32 recv_buffer_size = rp.Pop<u32>();
194 u8 data_channel; 239 u8 data_channel = rp.Pop<u8>();
195 rp.PopRaw(data_channel); 240 u16 network_node_id = rp.Pop<u16>();
196 u16 network_node_id;
197 rp.PopRaw(network_node_id);
198 241
199 // TODO(Subv): Store the data channel and verify it when receiving data frames. 242 // TODO(Subv): Store the data channel and verify it when receiving data frames.
200 243
@@ -251,13 +294,25 @@ static void BeginHostingNetwork(Interface* self) {
251 ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); 294 ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member.");
252 295
253 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost); 296 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost);
297
298 // Ensure the application data size is less than the maximum value.
299 ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big.");
300
301 // Set up basic information for this network.
302 network_info.oui_value = NintendoOUI;
303 network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo);
304
254 connection_status.max_nodes = network_info.max_nodes; 305 connection_status.max_nodes = network_info.max_nodes;
255 306
307 // Resize the nodes list to hold max_nodes.
308 node_info.resize(network_info.max_nodes);
309
256 // There's currently only one node in the network (the host). 310 // There's currently only one node in the network (the host).
257 connection_status.total_nodes = 1; 311 connection_status.total_nodes = 1;
312 network_info.total_nodes = 1;
258 // The host is always the first node 313 // The host is always the first node
259 connection_status.network_node_id = 1; 314 connection_status.network_node_id = 1;
260 node_info.network_node_id = 1; 315 node_info[0].network_node_id = 1;
261 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. 316 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken.
262 connection_status.node_bitmask |= 1; 317 connection_status.node_bitmask |= 1;
263 318
@@ -325,7 +380,7 @@ static void GetChannel(Interface* self) {
325 u8 channel = is_connected ? network_channel : 0; 380 u8 channel = is_connected ? network_channel : 0;
326 381
327 rb.Push(RESULT_SUCCESS); 382 rb.Push(RESULT_SUCCESS);
328 rb.PushRaw(channel); 383 rb.Push(channel);
329 384
330 LOG_DEBUG(Service_NWM, "called"); 385 LOG_DEBUG(Service_NWM, "called");
331} 386}
@@ -373,7 +428,8 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) {
373 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) 428 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost))
374 return; 429 return;
375 430
376 // TODO(Subv): Actually generate the beacon and send it. 431 // TODO(Subv): Actually send the beacon.
432 std::vector<u8> frame = GenerateBeaconFrame(network_info, node_info);
377 433
378 // Start broadcasting the network, send a beacon frame every 102.4ms. 434 // Start broadcasting the network, send a beacon frame every 102.4ms.
379 CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late, 435 CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late,
diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h
index 65349f9fd..29b146569 100644
--- a/src/core/hle/service/nwm/nwm_uds.h
+++ b/src/core/hle/service/nwm/nwm_uds.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <vector>
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/swap.h" 11#include "common/swap.h"
11#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
@@ -33,6 +34,8 @@ struct NodeInfo {
33 34
34static_assert(sizeof(NodeInfo) == 40, "NodeInfo has incorrect size."); 35static_assert(sizeof(NodeInfo) == 40, "NodeInfo has incorrect size.");
35 36
37using NodeList = std::vector<NodeInfo>;
38
36enum class NetworkStatus { 39enum class NetworkStatus {
37 NotConnected = 3, 40 NotConnected = 3,
38 ConnectedAsHost = 6, 41 ConnectedAsHost = 6,
@@ -75,6 +78,8 @@ struct NetworkInfo {
75 std::array<u8, ApplicationDataSize> application_data; 78 std::array<u8, ApplicationDataSize> application_data;
76}; 79};
77 80
81static_assert(offsetof(NetworkInfo, oui_value) == 0xC, "oui_value is at the wrong offset.");
82static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset.");
78static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size."); 83static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size.");
79 84
80class NWM_UDS final : public Interface { 85class NWM_UDS final : public Interface {
diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp
new file mode 100644
index 000000000..c6e5bc5f1
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_beacon.cpp
@@ -0,0 +1,333 @@
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
7#include "core/hle/service/nwm/nwm_uds.h"
8#include "core/hle/service/nwm/uds_beacon.h"
9
10#include <cryptopp/aes.h>
11#include <cryptopp/md5.h>
12#include <cryptopp/modes.h>
13#include <cryptopp/sha.h>
14
15namespace Service {
16namespace NWM {
17
18// 802.11 broadcast MAC address
19constexpr MacAddress BroadcastMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
20
21constexpr u64 DefaultNetworkUptime = 900000000; // 15 minutes in microseconds.
22
23// Note: These values were taken from a packet capture of an o3DS XL
24// broadcasting a Super Smash Bros. 4 lobby.
25constexpr u16 DefaultExtraCapabilities = 0x0431;
26
27// Size of the SSID broadcast by an UDS beacon frame.
28constexpr u8 UDSBeaconSSIDSize = 8;
29
30// The maximum size of the data stored in the EncryptedData0 tag (24).
31constexpr u32 EncryptedDataSizeCutoff = 0xFA;
32
33/**
34 * NWM Beacon data encryption key, taken from the NWM module code.
35 * We stub this with an all-zeros key as that is enough for Citra's purpose.
36 * The real key can be used here to generate beacons that will be accepted by
37 * a real 3ds.
38 */
39constexpr std::array<u8, CryptoPP::AES::BLOCKSIZE> nwm_beacon_key = {};
40
41/**
42 * Generates a buffer with the fixed parameters of an 802.11 Beacon frame
43 * using dummy values.
44 * @returns A buffer with the fixed parameters of the beacon frame.
45 */
46std::vector<u8> GenerateFixedParameters() {
47 std::vector<u8> buffer(sizeof(BeaconFrameHeader));
48
49 BeaconFrameHeader header{};
50 // Use a fixed default time for now.
51 // TODO(Subv): Perhaps use the difference between now and the time the network was started?
52 header.timestamp = DefaultNetworkUptime;
53 header.beacon_interval = DefaultBeaconInterval;
54 header.capabilities = DefaultExtraCapabilities;
55
56 std::memcpy(buffer.data(), &header, sizeof(header));
57
58 return buffer;
59}
60
61/**
62 * Generates an SSID tag of an 802.11 Beacon frame with an 8-byte all-zero SSID value.
63 * @returns A buffer with the SSID tag.
64 */
65std::vector<u8> GenerateSSIDTag() {
66 std::vector<u8> buffer(sizeof(TagHeader) + UDSBeaconSSIDSize);
67
68 TagHeader tag_header{};
69 tag_header.tag_id = static_cast<u8>(TagId::SSID);
70 tag_header.length = UDSBeaconSSIDSize;
71
72 std::memcpy(buffer.data(), &tag_header, sizeof(TagHeader));
73
74 // The rest of the buffer is already filled with zeros.
75
76 return buffer;
77}
78
79/**
80 * Generates a buffer with the basic tagged parameters of an 802.11 Beacon frame
81 * such as SSID, Rate Information, Country Information, etc.
82 * @returns A buffer with the tagged parameters of the beacon frame.
83 */
84std::vector<u8> GenerateBasicTaggedParameters() {
85 // Append the SSID tag
86 std::vector<u8> buffer = GenerateSSIDTag();
87
88 // TODO(Subv): Add the SupportedRates tag.
89 // TODO(Subv): Add the DSParameterSet tag.
90 // TODO(Subv): Add the TrafficIndicationMap tag.
91 // TODO(Subv): Add the CountryInformation tag.
92 // TODO(Subv): Add the ERPInformation tag.
93
94 return buffer;
95}
96
97/**
98 * Generates a buffer with the Dummy Nintendo tag.
99 * It is currently unknown what this tag does.
100 * TODO(Subv): Figure out if this is needed and what it does.
101 * @returns A buffer with the Nintendo tagged parameters of the beacon frame.
102 */
103std::vector<u8> GenerateNintendoDummyTag() {
104 // Note: These values were taken from a packet capture of an o3DS XL
105 // broadcasting a Super Smash Bros. 4 lobby.
106 constexpr std::array<u8, 3> dummy_data = {0x0A, 0x00, 0x00};
107
108 DummyTag tag{};
109 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
110 tag.header.length = sizeof(DummyTag) - sizeof(TagHeader);
111 tag.oui_type = static_cast<u8>(NintendoTagId::Dummy);
112 tag.oui = NintendoOUI;
113 tag.data = dummy_data;
114
115 std::vector<u8> buffer(sizeof(DummyTag));
116 std::memcpy(buffer.data(), &tag, sizeof(DummyTag));
117
118 return buffer;
119}
120
121/**
122 * Generates a buffer with the Network Info Nintendo tag.
123 * This tag contains the network information of the network that is being broadcast.
124 * It also contains the application data provided by the application that opened the network.
125 * @returns A buffer with the Nintendo network info parameter of the beacon frame.
126 */
127std::vector<u8> GenerateNintendoNetworkInfoTag(const NetworkInfo& network_info) {
128 NetworkInfoTag tag{};
129 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
130 tag.header.length =
131 sizeof(NetworkInfoTag) - sizeof(TagHeader) + network_info.application_data_size;
132 tag.appdata_size = network_info.application_data_size;
133 // Set the hash to zero initially, it will be updated once we calculate it.
134 tag.sha_hash = {};
135
136 // Ensure the network structure has the correct OUI and OUI type.
137 ASSERT(network_info.oui_type == static_cast<u8>(NintendoTagId::NetworkInfo));
138 ASSERT(network_info.oui_value == NintendoOUI);
139
140 // Ensure the application data size is less than the maximum value.
141 ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big.");
142
143 // This tag contains the network info structure starting at the OUI.
144 std::memcpy(tag.network_info.data(), &network_info.oui_value, tag.network_info.size());
145
146 // Copy the tag and the data so we can calculate the SHA1 over it.
147 std::vector<u8> buffer(sizeof(tag) + network_info.application_data_size);
148 std::memcpy(buffer.data(), &tag, sizeof(tag));
149 std::memcpy(buffer.data() + sizeof(tag), network_info.application_data.data(),
150 network_info.application_data_size);
151
152 // Calculate the SHA1 of the contents of the tag.
153 std::array<u8, CryptoPP::SHA1::DIGESTSIZE> hash;
154 CryptoPP::SHA1().CalculateDigest(hash.data(),
155 buffer.data() + offsetof(NetworkInfoTag, network_info),
156 buffer.size() - sizeof(TagHeader));
157
158 // Copy it directly into the buffer, overwriting the zeros that we had previously placed there.
159 std::memcpy(buffer.data() + offsetof(NetworkInfoTag, sha_hash), hash.data(), hash.size());
160
161 return buffer;
162}
163
164/*
165 * Calculates the CTR used for the AES-CTR encryption of the data stored in the
166 * EncryptedDataTags.
167 * @returns The CTR used for beacon crypto.
168 */
169std::array<u8, CryptoPP::AES::BLOCKSIZE> GetBeaconCryptoCTR(const NetworkInfo& network_info) {
170 BeaconDataCryptoCTR data{};
171
172 data.host_mac = network_info.host_mac_address;
173 data.wlan_comm_id = network_info.wlan_comm_id;
174 data.id = network_info.id;
175 data.network_id = network_info.network_id;
176
177 std::array<u8, CryptoPP::AES::BLOCKSIZE> hash;
178 std::memcpy(hash.data(), &data, sizeof(data));
179
180 return hash;
181}
182
183/*
184 * Serializes the node information into the format needed for network transfer,
185 * and then encrypts it with the NWM key.
186 * @returns The serialized and encrypted node information.
187 */
188std::vector<u8> GeneratedEncryptedData(const NetworkInfo& network_info, const NodeList& nodes) {
189 std::vector<u8> buffer(sizeof(BeaconData));
190
191 BeaconData data{};
192 std::memcpy(buffer.data(), &data, sizeof(BeaconData));
193
194 for (const NodeInfo& node : nodes) {
195 // Serialize each node and convert the data from
196 // host byte-order to Big Endian.
197 BeaconNodeInfo info{};
198 info.friend_code_seed = node.friend_code_seed;
199 info.network_node_id = node.network_node_id;
200 for (int i = 0; i < info.username.size(); ++i)
201 info.username[i] = node.username[i];
202
203 buffer.insert(buffer.end(), reinterpret_cast<u8*>(&info),
204 reinterpret_cast<u8*>(&info) + sizeof(info));
205 }
206
207 // Calculate the MD5 hash of the data in the buffer, not including the hash field.
208 std::array<u8, CryptoPP::MD5::DIGESTSIZE> hash;
209 CryptoPP::MD5().CalculateDigest(hash.data(), buffer.data() + offsetof(BeaconData, bitmask),
210 buffer.size() - sizeof(data.md5_hash));
211
212 // Copy the hash into the buffer.
213 std::memcpy(buffer.data(), hash.data(), hash.size());
214
215 // Encrypt the data using AES-CTR and the NWM beacon key.
216 using CryptoPP::AES;
217 std::array<u8, AES::BLOCKSIZE> counter = GetBeaconCryptoCTR(network_info);
218 CryptoPP::CTR_Mode<AES>::Encryption aes;
219 aes.SetKeyWithIV(nwm_beacon_key.data(), AES::BLOCKSIZE, counter.data());
220 aes.ProcessData(buffer.data(), buffer.data(), buffer.size());
221
222 return buffer;
223}
224
225void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer) {
226 // Decrypt the data using AES-CTR and the NWM beacon key.
227 using CryptoPP::AES;
228 std::array<u8, AES::BLOCKSIZE> counter = GetBeaconCryptoCTR(network_info);
229 CryptoPP::CTR_Mode<AES>::Decryption aes;
230 aes.SetKeyWithIV(nwm_beacon_key.data(), AES::BLOCKSIZE, counter.data());
231 aes.ProcessData(buffer.data(), buffer.data(), buffer.size());
232}
233
234/**
235 * Generates a buffer with the Network Info Nintendo tag.
236 * This tag contains the first portion of the encrypted payload in the 802.11 beacon frame.
237 * The encrypted payload contains information about the nodes currently connected to the network.
238 * @returns A buffer with the first Nintendo encrypted data parameters of the beacon frame.
239 */
240std::vector<u8> GenerateNintendoFirstEncryptedDataTag(const NetworkInfo& network_info,
241 const NodeList& nodes) {
242 const size_t payload_size =
243 std::min<size_t>(EncryptedDataSizeCutoff, nodes.size() * sizeof(NodeInfo));
244
245 EncryptedDataTag tag{};
246 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
247 tag.header.length = sizeof(tag) - sizeof(TagHeader) + payload_size;
248 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData0);
249 tag.oui = NintendoOUI;
250
251 std::vector<u8> buffer(sizeof(tag) + payload_size);
252 std::memcpy(buffer.data(), &tag, sizeof(tag));
253
254 std::vector<u8> encrypted_data = GeneratedEncryptedData(network_info, nodes);
255 std::memcpy(buffer.data() + sizeof(tag), encrypted_data.data(), payload_size);
256
257 return buffer;
258}
259
260/**
261 * Generates a buffer with the Network Info Nintendo tag.
262 * This tag contains the second portion of the encrypted payload in the 802.11 beacon frame.
263 * The encrypted payload contains information about the nodes currently connected to the network.
264 * This tag is only present if the payload size is greater than EncryptedDataSizeCutoff (0xFA)
265 * bytes.
266 * @returns A buffer with the second Nintendo encrypted data parameters of the beacon frame.
267 */
268std::vector<u8> GenerateNintendoSecondEncryptedDataTag(const NetworkInfo& network_info,
269 const NodeList& nodes) {
270 // This tag is only present if the payload is larger than EncryptedDataSizeCutoff (0xFA).
271 if (nodes.size() * sizeof(NodeInfo) <= EncryptedDataSizeCutoff)
272 return {};
273
274 const size_t payload_size = nodes.size() * sizeof(NodeInfo) - EncryptedDataSizeCutoff;
275
276 const size_t tag_length = sizeof(EncryptedDataTag) - sizeof(TagHeader) + payload_size;
277
278 // TODO(Subv): What does the 3DS do when a game has too much data to fit into the tag?
279 ASSERT_MSG(tag_length <= 255, "Data is too big.");
280
281 EncryptedDataTag tag{};
282 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
283 tag.header.length = tag_length;
284 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData1);
285 tag.oui = NintendoOUI;
286
287 std::vector<u8> buffer(sizeof(tag) + payload_size);
288 std::memcpy(buffer.data(), &tag, sizeof(tag));
289
290 std::vector<u8> encrypted_data = GeneratedEncryptedData(network_info, nodes);
291 std::memcpy(buffer.data() + sizeof(tag), encrypted_data.data() + EncryptedDataSizeCutoff,
292 payload_size);
293
294 return buffer;
295}
296
297/**
298 * Generates a buffer with the Nintendo tagged parameters of an 802.11 Beacon frame
299 * for UDS communication.
300 * @returns A buffer with the Nintendo tagged parameters of the beacon frame.
301 */
302std::vector<u8> GenerateNintendoTaggedParameters(const NetworkInfo& network_info,
303 const NodeList& nodes) {
304 ASSERT_MSG(network_info.max_nodes == nodes.size(), "Inconsistent network state.");
305
306 std::vector<u8> buffer = GenerateNintendoDummyTag();
307 std::vector<u8> network_info_tag = GenerateNintendoNetworkInfoTag(network_info);
308 std::vector<u8> first_data_tag = GenerateNintendoFirstEncryptedDataTag(network_info, nodes);
309 std::vector<u8> second_data_tag = GenerateNintendoSecondEncryptedDataTag(network_info, nodes);
310
311 buffer.insert(buffer.end(), network_info_tag.begin(), network_info_tag.end());
312 buffer.insert(buffer.end(), first_data_tag.begin(), first_data_tag.end());
313 buffer.insert(buffer.end(), second_data_tag.begin(), second_data_tag.end());
314
315 return buffer;
316}
317
318std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes) {
319 std::vector<u8> buffer = GenerateFixedParameters();
320 std::vector<u8> basic_tags = GenerateBasicTaggedParameters();
321 std::vector<u8> nintendo_tags = GenerateNintendoTaggedParameters(network_info, nodes);
322
323 buffer.insert(buffer.end(), basic_tags.begin(), basic_tags.end());
324 buffer.insert(buffer.end(), nintendo_tags.begin(), nintendo_tags.end());
325
326 return buffer;
327}
328
329std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender) {
330 return {};
331}
332} // namespace NWM
333} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_beacon.h b/src/core/hle/service/nwm/uds_beacon.h
new file mode 100644
index 000000000..6df4c4f47
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_beacon.h
@@ -0,0 +1,173 @@
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 <deque>
9#include <vector>
10#include "common/common_types.h"
11#include "common/swap.h"
12#include "core/hle/service/service.h"
13
14namespace Service {
15namespace NWM {
16
17using MacAddress = std::array<u8, 6>;
18
19/// The maximum number of nodes that can exist in an UDS session.
20constexpr u32 UDSMaxNodes = 16;
21constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32};
22
23/// Additional block tag ids in the Beacon frames
24enum class TagId : u8 {
25 SSID = 0,
26 SupportedRates = 1,
27 DSParameterSet = 2,
28 TrafficIndicationMap = 5,
29 CountryInformation = 7,
30 ERPInformation = 42,
31 VendorSpecific = 221
32};
33
34/**
35 * Internal vendor-specific tag ids as stored inside
36 * VendorSpecific blocks in the Beacon frames.
37 */
38enum class NintendoTagId : u8 {
39 Dummy = 20,
40 NetworkInfo = 21,
41 EncryptedData0 = 24,
42 EncryptedData1 = 25,
43};
44
45struct BeaconEntryHeader {
46 u32_le total_size;
47 INSERT_PADDING_BYTES(1);
48 u8 wifi_channel;
49 INSERT_PADDING_BYTES(2);
50 MacAddress mac_address;
51 INSERT_PADDING_BYTES(6);
52 u32_le unk_size;
53 u32_le header_size;
54};
55
56static_assert(sizeof(BeaconEntryHeader) == 0x1C, "BeaconEntryHeader has incorrect size.");
57
58struct BeaconDataReplyHeader {
59 u32_le max_output_size;
60 u32_le total_size;
61 u32_le total_entries;
62};
63
64static_assert(sizeof(BeaconDataReplyHeader) == 12, "BeaconDataReplyHeader has incorrect size.");
65
66#pragma pack(push, 1)
67struct BeaconFrameHeader {
68 // Number of microseconds the AP has been active.
69 u64_le timestamp;
70 // Interval between beacon transmissions, expressed in TU.
71 u16_le beacon_interval;
72 // Indicates the presence of optional capabilities.
73 u16_le capabilities;
74};
75#pragma pack(pop)
76
77static_assert(sizeof(BeaconFrameHeader) == 12, "BeaconFrameHeader has incorrect size.");
78
79struct TagHeader {
80 u8 tag_id;
81 u8 length;
82};
83
84static_assert(sizeof(TagHeader) == 2, "TagHeader has incorrect size.");
85
86struct DummyTag {
87 TagHeader header;
88 std::array<u8, 3> oui;
89 u8 oui_type;
90 std::array<u8, 3> data;
91};
92
93static_assert(sizeof(DummyTag) == 9, "DummyTag has incorrect size.");
94
95struct NetworkInfoTag {
96 TagHeader header;
97 std::array<u8, 0x1F> network_info;
98 std::array<u8, 0x14> sha_hash;
99 u8 appdata_size;
100};
101
102static_assert(sizeof(NetworkInfoTag) == 54, "NetworkInfoTag has incorrect size.");
103
104struct EncryptedDataTag {
105 TagHeader header;
106 std::array<u8, 3> oui;
107 u8 oui_type;
108};
109
110static_assert(sizeof(EncryptedDataTag) == 6, "EncryptedDataTag has incorrect size.");
111
112#pragma pack(push, 1)
113// The raw bytes of this structure are the CTR used in the encryption (AES-CTR)
114// of the beacon data stored in the EncryptedDataTags.
115struct BeaconDataCryptoCTR {
116 MacAddress host_mac;
117 u32_le wlan_comm_id;
118 u8 id;
119 INSERT_PADDING_BYTES(1);
120 u32_le network_id;
121};
122
123static_assert(sizeof(BeaconDataCryptoCTR) == 0x10, "BeaconDataCryptoCTR has incorrect size.");
124
125struct BeaconNodeInfo {
126 u64_be friend_code_seed;
127 std::array<u16_be, 10> username;
128 u16_be network_node_id;
129};
130
131static_assert(sizeof(BeaconNodeInfo) == 0x1E, "BeaconNodeInfo has incorrect size.");
132
133struct BeaconData {
134 std::array<u8, 0x10> md5_hash;
135 u16_be bitmask;
136};
137#pragma pack(pop)
138
139static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size.");
140
141/// Information about a received WiFi packet.
142/// Acts as our own 802.11 header.
143struct WifiPacket {
144 enum class PacketType { Beacon, Data };
145
146 PacketType type; ///< The type of 802.11 frame, Beacon / Data.
147
148 /// Raw 802.11 frame data, starting at the management frame header for management frames.
149 std::vector<u8> data;
150 MacAddress transmitter_address; ///< Mac address of the transmitter.
151 MacAddress destination_address; ///< Mac address of the receiver.
152 u8 channel; ///< WiFi channel where this frame was transmitted.
153};
154
155/**
156 * Decrypts the beacon data buffer for the network described by `network_info`.
157 */
158void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer);
159
160/**
161 * Generates an 802.11 beacon frame starting at the management frame header.
162 * This frame contains information about the network and its connected clients.
163 * @returns The generated frame.
164 */
165std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes);
166
167/**
168 * Returns a list of received 802.11 frames from the specified sender
169 * matching the type since the last call.
170 */
171std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender);
172} // namespace NWM
173} // namespace Service