summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp68
-rw-r--r--src/core/hle/service/nwm/uds_data.cpp112
-rw-r--r--src/core/hle/service/nwm/uds_data.h80
4 files changed, 261 insertions, 1 deletions
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/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
index e92900d48..f6125825f 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,71 @@ 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 // TODO(Subv): Figure out the error if this is called while not connected to a network.
408 if (connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsClient) ||
409 connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
410 ASSERT_MSG(false, "Not connected to a network (unimplemented)");
411 }
412
413 // TODO(Subv): Do something with the flags.
414
415 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
416
417 constexpr size_t MaxSize = 0x5C6;
418 if (data_size > MaxSize) {
419 rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS,
420 ErrorSummary::WrongArgument, ErrorLevel::Usage));
421 return;
422 }
423
424 std::vector<u8> data(data_size);
425 Memory::ReadBlock(input_address, data.data(), data.size());
426
427 // TODO(Subv): Increment the sequence number after each sent packet.
428 u16 sequence_number = 0;
429 std::vector<u8> data_frame = GenerateDataFrame(data, data_channel, dest_node_id,
430 connection_status.network_node_id,
431 sequence_number);
432
433 // TODO(Subv): Send the frame.
434
435 rb.Push(RESULT_SUCCESS);
436
437 LOG_WARNING(Service_NWM, "(STUB) called dest_node_id=%u size=%u flags=%u channel=%u",
438 static_cast<u32>(dest_node_id), data_size, flags, static_cast<u32>(data_channel));
439}
440
441/**
376 * NWM_UDS::GetChannel service function. 442 * NWM_UDS::GetChannel service function.
377 * Returns the WiFi channel in which the network we're connected to is transmitting. 443 * Returns the WiFi channel in which the network we're connected to is transmitting.
378 * Inputs: 444 * Inputs:
@@ -564,7 +630,7 @@ const Interface::FunctionInfo FunctionTable[] = {
564 {0x00130040, nullptr, "Unbind"}, 630 {0x00130040, nullptr, "Unbind"},
565 {0x001400C0, nullptr, "PullPacket"}, 631 {0x001400C0, nullptr, "PullPacket"},
566 {0x00150080, nullptr, "SetMaxSendDelay"}, 632 {0x00150080, nullptr, "SetMaxSendDelay"},
567 {0x00170182, nullptr, "SendTo"}, 633 {0x00170182, SendTo, "SendTo"},
568 {0x001A0000, GetChannel, "GetChannel"}, 634 {0x001A0000, GetChannel, "GetChannel"},
569 {0x001B0302, InitializeWithVersion, "InitializeWithVersion"}, 635 {0x001B0302, InitializeWithVersion, "InitializeWithVersion"},
570 {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"}, 636 {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..7e4aec624
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_data.cpp
@@ -0,0 +1,112 @@
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_data.h"
9#include "core/hw/aes/key.h"
10
11#include <cryptopp/aes.h>
12#include <cryptopp/md5.h>
13#include <cryptopp/modes.h>
14
15namespace Service {
16namespace NWM {
17
18// AES Keyslot used to generate the UDS data frame CCMP key.
19constexpr size_t UDSDataCryptoAESKeySlot = 0x2D;
20
21/*
22 * Generates a SNAP-enabled 802.2 LLC header for the specified protocol.
23 * @returns a buffer with the bytes of the generated header.
24 */
25static std::vector<u8> GenerateLLCHeader(EtherType protocol) {
26 LLCHeader header{};
27 header.protocol = static_cast<u16>(protocol);
28
29 std::vector<u8> buffer(sizeof(header));
30 memcpy(buffer.data(), &header, sizeof(header));
31
32 return buffer;
33}
34
35/*
36 * Generates a Nintendo UDS SecureData header with the specified parameters.
37 * @returns a buffer with the bytes of the generated header.
38 */
39static std::vector<u8> GenerateSecureDataHeader(u16 data_size, u8 channel, u16 dest_node_id,
40 u16 src_node_id, u16 sequence_number) {
41 SecureDataHeader header{};
42 header.protocol_size = data_size + sizeof(SecureDataHeader);
43 // TODO(Subv): It is likely that the first 4 bytes of this header are actually a decorator for another protocol.
44 header.securedata_size = data_size + sizeof(SecureDataHeader) - 4;
45 header.is_management = 0; // Frames sent by the emulated application are never UDS management frames
46 header.data_channel = channel;
47 header.sequence_number = sequence_number;
48 header.dest_node_id = dest_node_id;
49 header.src_node_id = src_node_id;
50
51 std::vector<u8> buffer(sizeof(header));
52 memcpy(buffer.data(), &header, sizeof(header));
53
54 return buffer;
55}
56
57/*
58 * Calculates the CTR used for the AES-CTR process that calculates
59 * the CCMP crypto key for data frames.
60 * @returns The CTR used for data frames crypto key generation.
61 */
62static std::array<u8, CryptoPP::MD5::DIGESTSIZE> GetDataCryptoCTR(const NetworkInfo& network_info) {
63 DataFrameCryptoCTR data{};
64
65 data.host_mac = network_info.host_mac_address;
66 data.wlan_comm_id = network_info.wlan_comm_id;
67 data.id = network_info.id;
68 data.network_id = network_info.network_id;
69
70 std::array<u8, CryptoPP::MD5::DIGESTSIZE> hash;
71 CryptoPP::MD5().CalculateDigest(hash.data(), reinterpret_cast<u8*>(&data), sizeof(data));
72
73 return hash;
74}
75
76/*
77 * Generates the key used for encrypting the 802.11 data frames generated by UDS.
78 * @returns The key used for data frames crypto.
79 */
80static std::array<u8, CryptoPP::AES::BLOCKSIZE> GenerateDataCCMPKey(const std::vector<u8>& passphrase,
81 const NetworkInfo& network_info) {
82 // Calculate the MD5 hash of the input passphrase.
83 std::array<u8, CryptoPP::MD5::DIGESTSIZE> passphrase_hash;
84 CryptoPP::MD5().CalculateDigest(passphrase_hash.data(), passphrase.data(), passphrase.size());
85
86 std::array<u8, CryptoPP::AES::BLOCKSIZE> ccmp_key;
87
88 // The CCMP key is the result of encrypting the MD5 hash of the passphrase with AES-CTR using keyslot 0x2D.
89 using CryptoPP::AES;
90 std::array<u8, CryptoPP::MD5::DIGESTSIZE> counter = GetDataCryptoCTR(network_info);
91 std::array<u8, AES::BLOCKSIZE> key = HW::AES::GetNormalKey(UDSDataCryptoAESKeySlot);
92 CryptoPP::CTR_Mode<AES>::Encryption aes;
93 aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, counter.data());
94 aes.ProcessData(ccmp_key.data(), passphrase_hash.data(), passphrase_hash.size());
95
96 return ccmp_key;
97}
98
99std::vector<u8> GenerateDataFrame(const std::vector<u8>& data, u8 channel, u16 dest_node, u16 src_node, u16 sequence_number) {
100 std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData);
101 std::vector<u8> securedata_header = GenerateSecureDataHeader(data.size(), channel, dest_node, src_node, sequence_number);
102
103 buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end());
104 buffer.insert(buffer.end(), data.begin(), data.end());
105 // TODO(Subv): Encrypt the frame.
106 // TODO(Subv): Prepend CCMP initialization vector (sequence_number).
107 // TODO(Subv): Encapsulate the frame in an 802.11 data frame.
108 return buffer;
109}
110
111} // namespace NWM
112} // 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..0dd46bcb1
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_data.h
@@ -0,0 +1,80 @@
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 "common/common_types.h"
8#include "common/swap.h"
9#include "core/hle/service/service.h"
10
11namespace Service {
12namespace NWM {
13
14enum class SAP : u8 {
15 SNAPExtensionUsed = 0xAA
16};
17
18enum class PDUControl : u8 {
19 UnnumberedInformation = 3
20};
21
22enum class EtherType : u16 {
23 SecureData = 0x876D,
24 EAPoL = 0x888E
25};
26
27/*
28 * 802.2 header, UDS packets always use SNAP for these headers,
29 * which means the dsap and ssap are always SNAPExtensionUsed (0xAA)
30 * and the OUI is always 0.
31 */
32struct LLCHeader {
33 u8 dsap = static_cast<u8>(SAP::SNAPExtensionUsed);
34 u8 ssap = static_cast<u8>(SAP::SNAPExtensionUsed);
35 u8 control = static_cast<u8>(PDUControl::UnnumberedInformation);
36 std::array<u8, 3> OUI = {};
37 u16_be protocol;
38};
39
40static_assert(sizeof(LLCHeader) == 8, "LLCHeader has the wrong size");
41
42/*
43 * Nintendo SecureData header, every UDS packet contains one,
44 * it is used to store metadata about the transmission such as
45 * the source and destination network node ids.
46 */
47struct SecureDataHeader {
48 u16_be protocol_size;
49 INSERT_PADDING_BYTES(2);
50 u16_be securedata_size;
51 u8 is_management;
52 u8 data_channel;
53 u16_be sequence_number;
54 u16_be dest_node_id;
55 u16_be src_node_id;
56};
57
58static_assert(sizeof(SecureDataHeader) == 14, "SecureDataHeader has the wrong size");
59
60/*
61 * The raw bytes of this structure are the CTR used in the encryption (AES-CTR)
62 * process used to generate the CCMP key for data frame encryption.
63 */
64struct DataFrameCryptoCTR {
65 u32_le wlan_comm_id;
66 u32_le network_id;
67 std::array<u8, 6> host_mac;
68 u16_le id;
69};
70
71static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size");
72
73/**
74 * Generates an encrypted 802.11 data frame starting at the CCMP IV.
75 * @returns The generated frame.
76 */
77std::vector<u8> GenerateDataFrame(const std::vector<u8>& data, u8 channel, u16 dest_node, u16 src_node, u16 sequence_number);
78
79} // namespace NWM
80} // namespace Service