diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/service/nwm/nwm_uds.cpp | 9 | ||||
| -rw-r--r-- | src/core/hle/service/nwm/uds_data.cpp | 148 | ||||
| -rw-r--r-- | src/core/hle/service/nwm/uds_data.h | 11 |
3 files changed, 156 insertions, 12 deletions
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index c43c5ca44..d9bd9c4a4 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp | |||
| @@ -433,9 +433,12 @@ static void SendTo(Interface* self) { | |||
| 433 | 433 | ||
| 434 | // TODO(Subv): Increment the sequence number after each sent packet. | 434 | // TODO(Subv): Increment the sequence number after each sent packet. |
| 435 | u16 sequence_number = 0; | 435 | u16 sequence_number = 0; |
| 436 | std::vector<u8> data_frame = GenerateDataFrame(data, data_channel, dest_node_id, | 436 | std::vector<u8> data_payload = GenerateDataPayload(data, data_channel, dest_node_id, |
| 437 | connection_status.network_node_id, | 437 | connection_status.network_node_id, |
| 438 | sequence_number); | 438 | sequence_number); |
| 439 | |||
| 440 | // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt | ||
| 441 | // and encapsulate the payload. | ||
| 439 | 442 | ||
| 440 | // TODO(Subv): Send the frame. | 443 | // TODO(Subv): Send the frame. |
| 441 | 444 | ||
diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp index 9ba2fdcf1..e05ca8815 100644 --- a/src/core/hle/service/nwm/uds_data.cpp +++ b/src/core/hle/service/nwm/uds_data.cpp | |||
| @@ -5,10 +5,12 @@ | |||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | 6 | ||
| 7 | #include "core/hle/service/nwm/nwm_uds.h" | 7 | #include "core/hle/service/nwm/nwm_uds.h" |
| 8 | #include "core/hle/service/nwm/uds_beacon.h" | ||
| 8 | #include "core/hle/service/nwm/uds_data.h" | 9 | #include "core/hle/service/nwm/uds_data.h" |
| 9 | #include "core/hw/aes/key.h" | 10 | #include "core/hw/aes/key.h" |
| 10 | 11 | ||
| 11 | #include <cryptopp/aes.h> | 12 | #include <cryptopp/ccm.h> |
| 13 | #include <cryptopp/filters.h> | ||
| 12 | #include <cryptopp/md5.h> | 14 | #include <cryptopp/md5.h> |
| 13 | #include <cryptopp/modes.h> | 15 | #include <cryptopp/modes.h> |
| 14 | 16 | ||
| @@ -98,15 +100,149 @@ static std::array<u8, CryptoPP::AES::BLOCKSIZE> GenerateDataCCMPKey(const std::v | |||
| 98 | return ccmp_key; | 100 | return ccmp_key; |
| 99 | } | 101 | } |
| 100 | 102 | ||
| 101 | std::vector<u8> GenerateDataFrame(const std::vector<u8>& data, u8 channel, u16 dest_node, u16 src_node, u16 sequence_number) { | 103 | /* |
| 104 | * Generates the Additional Authenticated Data (AAD) for an UDS 802.11 encrypted data frame. | ||
| 105 | * @returns a buffer with the bytes of the AAD. | ||
| 106 | */ | ||
| 107 | static std::vector<u8> GenerateCCMPAAD(const MacAddress& sender, const MacAddress& receiver) { | ||
| 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 receiver; | ||
| 118 | MacAddress transmitter; | ||
| 119 | MacAddress destination; | ||
| 120 | u16_be SC; // MPDU Sequence Control field | ||
| 121 | } aad_struct{}; | ||
| 122 | |||
| 123 | // Default FC value of DataFrame | Protected | ToDS | ||
| 124 | constexpr u16 DefaultFrameControl = 0x0841; | ||
| 125 | |||
| 126 | aad_struct.FC = DefaultFrameControl; | ||
| 127 | aad_struct.SC = 0; | ||
| 128 | aad_struct.transmitter = sender; | ||
| 129 | aad_struct.receiver = receiver; | ||
| 130 | aad_struct.destination = receiver; | ||
| 131 | |||
| 132 | std::vector<u8> aad(sizeof(aad_struct)); | ||
| 133 | std::memcpy(aad.data(), &aad_struct, sizeof(aad_struct)); | ||
| 134 | |||
| 135 | return aad; | ||
| 136 | } | ||
| 137 | |||
| 138 | /* | ||
| 139 | * Decrypts the payload of an encrypted 802.11 data frame using the specified key. | ||
| 140 | * @returns The decrypted payload. | ||
| 141 | */ | ||
| 142 | static std::vector<u8> DecryptDataFrame(const std::vector<u8>& encrypted_payload, const std::array<u8, CryptoPP::AES::BLOCKSIZE>& ccmp_key, | ||
| 143 | const MacAddress& sender, const MacAddress& receiver, u16 sequence_number) { | ||
| 144 | |||
| 145 | // Reference: IEEE 802.11-2007 | ||
| 146 | |||
| 147 | std::vector<u8> aad = GenerateCCMPAAD(sender, receiver); | ||
| 148 | |||
| 149 | std::vector<u8> packet_number{0, 0, 0, 0, | ||
| 150 | static_cast<u8>((sequence_number >> 8) & 0xFF), | ||
| 151 | static_cast<u8>(sequence_number & 0xFF)}; | ||
| 152 | |||
| 153 | // 8.3.3.3.3 Construct CCM nonce (13 bytes) | ||
| 154 | std::vector<u8> nonce; | ||
| 155 | nonce.push_back(0); // priority | ||
| 156 | nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2 | ||
| 157 | nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN | ||
| 158 | |||
| 159 | try { | ||
| 160 | CryptoPP::CCM<CryptoPP::AES, 8>::Decryption d; | ||
| 161 | d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size()); | ||
| 162 | d.SpecifyDataLengths(aad.size(), encrypted_payload.size() - 8, 0); | ||
| 163 | |||
| 164 | CryptoPP::AuthenticatedDecryptionFilter df(d, nullptr, | ||
| 165 | CryptoPP::AuthenticatedDecryptionFilter::MAC_AT_END | | ||
| 166 | CryptoPP::AuthenticatedDecryptionFilter::THROW_EXCEPTION); | ||
| 167 | // put aad | ||
| 168 | df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size()); | ||
| 169 | |||
| 170 | // put cipher with mac | ||
| 171 | df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, encrypted_payload.data(), encrypted_payload.size() - 8); | ||
| 172 | df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, encrypted_payload.data() + encrypted_payload.size() - 8, 8); | ||
| 173 | |||
| 174 | df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL); | ||
| 175 | df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL); | ||
| 176 | df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); | ||
| 177 | |||
| 178 | int size = df.MaxRetrievable(); | ||
| 179 | |||
| 180 | std::vector<u8> pdata(size); | ||
| 181 | df.Get(pdata.data(), size); | ||
| 182 | return pdata; | ||
| 183 | } catch (CryptoPP::Exception&) { | ||
| 184 | LOG_ERROR(Service_NWM, "failed to decrypt"); | ||
| 185 | } | ||
| 186 | |||
| 187 | return {}; | ||
| 188 | } | ||
| 189 | |||
| 190 | /* | ||
| 191 | * Encrypts the payload of an 802.11 data frame using the specified key. | ||
| 192 | * @returns The encrypted payload. | ||
| 193 | */ | ||
| 194 | static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload, const std::array<u8, CryptoPP::AES::BLOCKSIZE>& ccmp_key, | ||
| 195 | const MacAddress& sender, const MacAddress& receiver, u16 sequence_number) { | ||
| 196 | // Reference: IEEE 802.11-2007 | ||
| 197 | |||
| 198 | std::vector<u8> aad = GenerateCCMPAAD(sender, receiver); | ||
| 199 | |||
| 200 | std::vector<u8> packet_number{0, 0, 0, 0, | ||
| 201 | static_cast<u8>((sequence_number >> 8) & 0xFF), | ||
| 202 | static_cast<u8>(sequence_number & 0xFF)}; | ||
| 203 | |||
| 204 | // 8.3.3.3.3 Construct CCM nonce (13 bytes) | ||
| 205 | std::vector<u8> nonce; | ||
| 206 | nonce.push_back(0); // priority | ||
| 207 | nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2 | ||
| 208 | nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN | ||
| 209 | |||
| 210 | try { | ||
| 211 | CryptoPP::CCM<CryptoPP::AES, 8>::Encryption d; | ||
| 212 | d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size()); | ||
| 213 | d.SpecifyDataLengths(aad.size(), payload.size(), 0); | ||
| 214 | |||
| 215 | CryptoPP::AuthenticatedEncryptionFilter df(d); | ||
| 216 | // put aad | ||
| 217 | df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size()); | ||
| 218 | df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL); | ||
| 219 | |||
| 220 | // put plaintext | ||
| 221 | df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, payload.data(), payload.size()); | ||
| 222 | df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL); | ||
| 223 | |||
| 224 | df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); | ||
| 225 | |||
| 226 | int size = df.MaxRetrievable(); | ||
| 227 | |||
| 228 | std::vector<u8> cipher(size); | ||
| 229 | df.Get(cipher.data(), size); | ||
| 230 | return cipher; | ||
| 231 | } catch (CryptoPP::Exception&) { | ||
| 232 | LOG_ERROR(Service_NWM, "failed to encrypt"); | ||
| 233 | } | ||
| 234 | |||
| 235 | return {}; | ||
| 236 | } | ||
| 237 | |||
| 238 | std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, u16 src_node, | ||
| 239 | u16 sequence_number) { | ||
| 102 | std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData); | 240 | std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData); |
| 103 | std::vector<u8> securedata_header = GenerateSecureDataHeader(data.size(), channel, dest_node, src_node, sequence_number); | 241 | std::vector<u8> securedata_header = GenerateSecureDataHeader(data.size(), channel, dest_node, src_node, |
| 242 | sequence_number); | ||
| 104 | 243 | ||
| 105 | buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end()); | 244 | buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end()); |
| 106 | buffer.insert(buffer.end(), data.begin(), data.end()); | 245 | buffer.insert(buffer.end(), data.begin(), data.end()); |
| 107 | // TODO(Subv): Encrypt the frame. | ||
| 108 | // TODO(Subv): Prepend CCMP initialization vector (sequence_number). | ||
| 109 | // TODO(Subv): Encapsulate the frame in an 802.11 data frame. | ||
| 110 | return buffer; | 246 | return buffer; |
| 111 | } | 247 | } |
| 112 | 248 | ||
diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h index 8480ef94b..960f13cee 100644 --- a/src/core/hle/service/nwm/uds_data.h +++ b/src/core/hle/service/nwm/uds_data.h | |||
| @@ -4,10 +4,15 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 7 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 8 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 9 | #include "core/hle/service/service.h" | 12 | #include "core/hle/service/service.h" |
| 10 | 13 | ||
| 14 | #include <cryptopp/aes.h> | ||
| 15 | |||
| 11 | namespace Service { | 16 | namespace Service { |
| 12 | namespace NWM { | 17 | namespace NWM { |
| 13 | 18 | ||
| @@ -73,10 +78,10 @@ struct DataFrameCryptoCTR { | |||
| 73 | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); | 78 | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); |
| 74 | 79 | ||
| 75 | /** | 80 | /** |
| 76 | * Generates an encrypted 802.11 data frame starting at the CCMP IV. | 81 | * Generates an unencrypted 802.11 data payload. |
| 77 | * @returns The generated frame. | 82 | * @returns The generated frame payload. |
| 78 | */ | 83 | */ |
| 79 | std::vector<u8> GenerateDataFrame(const std::vector<u8>& data, u8 channel, u16 dest_node, u16 src_node, u16 sequence_number); | 84 | std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, u16 src_node, u16 sequence_number); |
| 80 | 85 | ||
| 81 | } // namespace NWM | 86 | } // namespace NWM |
| 82 | } // namespace Service | 87 | } // namespace Service |