Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
90
tools/EVerest-main/lib/everest/slac/src/channel.cpp
Normal file
90
tools/EVerest-main/lib/everest/slac/src/channel.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2022 - 2022 Pionix GmbH and Contributors to EVerest
|
||||
#include <slac/channel.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include "packet_socket.hpp"
|
||||
|
||||
namespace slac {
|
||||
|
||||
Channel::Channel() : socket(nullptr){};
|
||||
|
||||
bool Channel::open(const std::string& interface_name) {
|
||||
did_timeout = false;
|
||||
|
||||
auto if_info = ::utils::InterfaceInfo(interface_name);
|
||||
if (!if_info.is_valid()) {
|
||||
error = if_info.get_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(orig_if_mac, if_info.get_mac(), sizeof(orig_if_mac));
|
||||
|
||||
socket = std::make_unique<::utils::PacketSocket>(if_info, defs::ETH_P_HOMEPLUG_GREENPHY);
|
||||
if (!socket->is_valid()) {
|
||||
error = socket->get_error();
|
||||
socket.reset();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Channel::~Channel() = default;
|
||||
|
||||
bool Channel::read(slac::messages::HomeplugMessage& msg, int timeout) {
|
||||
did_timeout = false;
|
||||
using IOResult = ::utils::PacketSocket::IOResult;
|
||||
if (socket) {
|
||||
switch (socket->read(reinterpret_cast<uint8_t*>(msg.get_raw_message_ptr()), timeout)) {
|
||||
// FIXME (aw): this enum conversion looks ugly
|
||||
case IOResult::Failure:
|
||||
error = socket->get_error();
|
||||
return false;
|
||||
case IOResult::Timeout:
|
||||
did_timeout = true;
|
||||
return false;
|
||||
case IOResult::Ok:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
error = "No IO socket available\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Channel::write(slac::messages::HomeplugMessage& msg, int timeout) {
|
||||
using IOResult = ::utils::PacketSocket::IOResult;
|
||||
|
||||
assert(("Homeplug message is not valid\n", msg.is_valid()));
|
||||
|
||||
did_timeout = false;
|
||||
|
||||
if (socket) {
|
||||
auto raw_msg_ether_shost = msg.get_src_mac();
|
||||
if (!msg.keep_source_mac()) {
|
||||
memcpy(raw_msg_ether_shost, orig_if_mac, sizeof(orig_if_mac));
|
||||
}
|
||||
switch (socket->write(msg.get_raw_message_ptr(), msg.get_raw_msg_len(), timeout)) {
|
||||
case IOResult::Failure:
|
||||
error = socket->get_error();
|
||||
return false;
|
||||
case IOResult::Timeout:
|
||||
did_timeout = true;
|
||||
return false;
|
||||
case IOResult::Ok:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
error = "No IO socket available\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t* Channel::get_mac_addr() {
|
||||
return orig_if_mac;
|
||||
}
|
||||
|
||||
} // namespace slac
|
||||
148
tools/EVerest-main/lib/everest/slac/src/packet_socket.cpp
Normal file
148
tools/EVerest-main/lib/everest/slac/src/packet_socket.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2022 - 2022 Pionix GmbH and Contributors to EVerest
|
||||
#include "packet_socket.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace utils {
|
||||
InterfaceInfo::InterfaceInfo(const std::string& interface_name) {
|
||||
// fetch all interfaces
|
||||
struct ifaddrs* if_addrs;
|
||||
int ret = getifaddrs(&if_addrs);
|
||||
if (ret == -1) {
|
||||
error = "Error while calling getifaddrs(): ";
|
||||
error += strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
// iterate through them and list them
|
||||
struct ifaddrs* cur_if_addr = if_addrs;
|
||||
while (cur_if_addr) {
|
||||
if (cur_if_addr->ifa_addr && cur_if_addr->ifa_addr->sa_family == AF_PACKET) {
|
||||
if (0 == interface_name.compare(cur_if_addr->ifa_name)) {
|
||||
const auto* addr_info = reinterpret_cast<struct sockaddr_ll*>(cur_if_addr->ifa_addr);
|
||||
memcpy(mac, addr_info->sll_addr, 6);
|
||||
interface_index = addr_info->sll_ifindex;
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cur_if_addr = cur_if_addr->ifa_next;
|
||||
}
|
||||
|
||||
freeifaddrs(if_addrs);
|
||||
|
||||
if (!valid) {
|
||||
error = "Interface " + interface_name + " not found";
|
||||
}
|
||||
}
|
||||
|
||||
PacketSocket::PacketSocket(const InterfaceInfo& if_info, int protocol) {
|
||||
// FIXME (aw): do we need to use O_NONBLOCKING?
|
||||
socket_fd = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, htons(protocol));
|
||||
|
||||
if (socket_fd == -1) {
|
||||
error = "Couldn't create the socket: ";
|
||||
error += strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
// bind this packet socket to a specific interface
|
||||
struct sockaddr_ll sock_addr = {
|
||||
AF_PACKET, // sll_family
|
||||
htons(protocol), // sll_protocol
|
||||
if_info.get_index(), // sll_ifindex
|
||||
0x00, // sll_hatype, set on receiving
|
||||
0x00, // sll_pkttype, set on receiving
|
||||
ETH_ALEN, // sll_halen
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // sll_addr[8]
|
||||
};
|
||||
|
||||
if (-1 == bind(socket_fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr))) {
|
||||
error = "Failed to bind the socket: ";
|
||||
error += strerror(errno);
|
||||
close(socket_fd);
|
||||
}
|
||||
|
||||
// everything should have worked out
|
||||
valid = true;
|
||||
}
|
||||
|
||||
PacketSocket::IOResult PacketSocket::read(uint8_t* buffer, int timeout) {
|
||||
struct pollfd poll_fd = {
|
||||
socket_fd, // file descriptor
|
||||
POLLIN, // requested event
|
||||
0 // returned event
|
||||
};
|
||||
int ret = poll(&poll_fd, 1, timeout);
|
||||
if (-1 == ret) {
|
||||
error = std::string("poll() failed with: ") + strerror(errno);
|
||||
return IOResult::Failure;
|
||||
}
|
||||
|
||||
if (0 == ret) {
|
||||
return IOResult::Timeout;
|
||||
}
|
||||
|
||||
if ((poll_fd.revents & POLLIN) == 0) {
|
||||
error = "poll() set other flag than POLLIN";
|
||||
return IOResult::Failure;
|
||||
}
|
||||
|
||||
bytes_read = ::read(socket_fd, buffer, MIN_BUFFER_SIZE);
|
||||
if (bytes_read == -1) {
|
||||
error = std::string("read() failed with: ") + strerror(errno);
|
||||
return IOResult::Failure;
|
||||
}
|
||||
|
||||
return IOResult::Ok;
|
||||
}
|
||||
|
||||
PacketSocket::IOResult PacketSocket::write(const void* buf, size_t size, int timeout) {
|
||||
struct pollfd poll_fd = {
|
||||
socket_fd, // file descriptor
|
||||
POLLOUT, // requested event
|
||||
0 // returned event
|
||||
};
|
||||
|
||||
int ret = poll(&poll_fd, 1, timeout);
|
||||
|
||||
if (ret == -1) {
|
||||
error = std::string("poll() failed with: ") + strerror(errno);
|
||||
return IOResult::Failure;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
return IOResult::Timeout;
|
||||
}
|
||||
|
||||
if ((poll_fd.revents & POLLOUT) == 0) {
|
||||
error = "poll() set other flag than POLLOUT";
|
||||
return IOResult::Failure;
|
||||
}
|
||||
|
||||
auto bytes_written = ::write(socket_fd, buf, size);
|
||||
|
||||
if (-1 == bytes_written) {
|
||||
error = std::string("write() failed with: ") + strerror(errno);
|
||||
return IOResult::Failure;
|
||||
}
|
||||
|
||||
if (bytes_written != size) {
|
||||
error = "write() only send part of the datagram - this should not happen";
|
||||
return IOResult::Failure;
|
||||
}
|
||||
|
||||
return IOResult::Ok;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
72
tools/EVerest-main/lib/everest/slac/src/packet_socket.hpp
Normal file
72
tools/EVerest-main/lib/everest/slac/src/packet_socket.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2022 - 2022 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef SRC_PACKET_SOCKET_HPP
|
||||
#define SRC_PACKET_SOCKET_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
namespace utils {
|
||||
class InterfaceInfo {
|
||||
public:
|
||||
explicit InterfaceInfo(const std::string& interface_name);
|
||||
bool is_valid() {
|
||||
return valid;
|
||||
};
|
||||
const std::string& get_error() const {
|
||||
return error;
|
||||
};
|
||||
|
||||
const int get_index() const {
|
||||
return interface_index;
|
||||
};
|
||||
const uint8_t* get_mac() const {
|
||||
return mac;
|
||||
};
|
||||
|
||||
private:
|
||||
bool valid{false};
|
||||
int interface_index{0};
|
||||
std::string error;
|
||||
uint8_t mac[ETH_ALEN];
|
||||
};
|
||||
|
||||
class PacketSocket {
|
||||
public:
|
||||
enum class IOResult {
|
||||
Ok,
|
||||
Failure,
|
||||
Timeout
|
||||
};
|
||||
|
||||
PacketSocket(const InterfaceInfo& if_info, int protocol);
|
||||
|
||||
bool is_valid() {
|
||||
return valid;
|
||||
};
|
||||
|
||||
const std::string& get_error() {
|
||||
return error;
|
||||
}
|
||||
|
||||
IOResult read(uint8_t* buffer, int timeout);
|
||||
|
||||
int get_last_read_size() const {
|
||||
return bytes_read;
|
||||
};
|
||||
|
||||
IOResult write(const void* buf, size_t count, int timeout);
|
||||
|
||||
static const int MIN_BUFFER_SIZE = ETH_FRAME_LEN;
|
||||
|
||||
private:
|
||||
int bytes_read{-1};
|
||||
bool valid{false};
|
||||
std::string error;
|
||||
int socket_fd{-1};
|
||||
};
|
||||
} // namespace utils
|
||||
|
||||
#endif // SRC_PACKET_SOCKET_HPP
|
||||
135
tools/EVerest-main/lib/everest/slac/src/slac.cpp
Normal file
135
tools/EVerest-main/lib/everest/slac/src/slac.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2022 - 2022 Pionix GmbH and Contributors to EVerest
|
||||
#include <slac/slac.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <endian.h>
|
||||
|
||||
#include <everest/tls/openssl_util.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace slac {
|
||||
namespace utils {
|
||||
|
||||
// note on byte order:
|
||||
// - sha256 takes the most significant byte first from the lowest
|
||||
// memory address
|
||||
// - for the generation of the aes-128, or NMK-HS, the first octet of
|
||||
// the sha256 output is taken as the zero octet for the NMK-HS
|
||||
// - for the generation of NID, the NMK is fed into sha256, so having
|
||||
// a const char* as input should be the proper byte ordering already
|
||||
void generate_nmk_hs(uint8_t nmk_hs[slac::defs::NMK_LEN], const char* plain_password, int password_len) {
|
||||
// do pbkdf1 (use sha256 as hashing function, iterate 1000 times,
|
||||
// use salt)
|
||||
std::vector<std::uint8_t> input(plain_password, plain_password + password_len);
|
||||
input.insert(input.end(), slac::defs::NMK_HASH_ARR.begin(), slac::defs::NMK_HASH_ARR.end());
|
||||
openssl::sha_256_digest_t digest;
|
||||
openssl::sha_256(input.data(), input.size(), digest);
|
||||
for (int i = 0; i < 1000 - 1; ++i) {
|
||||
openssl::sha_256(digest.data(), openssl::sha_256_digest_size, digest);
|
||||
}
|
||||
|
||||
memcpy(nmk_hs, digest.data(), slac::defs::NMK_LEN);
|
||||
}
|
||||
|
||||
void generate_nid_from_nmk(uint8_t nid[slac::defs::NID_LEN], const uint8_t nmk[slac::defs::NMK_LEN]) {
|
||||
// msb of least significant octet of NMK should be the leftmost bit
|
||||
// of the input, which corresponds to the usual const char* order
|
||||
|
||||
// do pbkdf1 (use sha256 as hashing function, iterate 5 times, no
|
||||
// salt)
|
||||
openssl::sha_256_digest_t digest;
|
||||
openssl::sha_256(nmk, slac::defs::NMK_LEN, digest);
|
||||
for (int i = 0; i < 5 - 1; ++i) {
|
||||
openssl::sha_256(digest.data(), openssl::sha_256_digest_size, digest);
|
||||
}
|
||||
|
||||
// use leftmost 52 bits of the hash output
|
||||
// left most bit should be bit 7 of the nid
|
||||
memcpy(nid, digest.data(), slac::defs::NID_LEN - 1); // (bits 52 - 5)
|
||||
nid[slac::defs::NID_LEN - 1] =
|
||||
(slac::defs::NID_SECURITY_LEVEL_SIMPLE_CONNECT << slac::defs::NID_SECURITY_LEVEL_OFFSET) |
|
||||
((static_cast<uint8_t>(digest.data()[6])) >> slac::defs::NID_MOST_SIGNIFANT_BYTE_SHIFT);
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
|
||||
namespace messages {
|
||||
|
||||
static constexpr auto effective_payload_length(const defs::MMV mmv) {
|
||||
if (mmv == defs::MMV::AV_1_0) {
|
||||
return sizeof(homeplug_message::payload);
|
||||
} else {
|
||||
return sizeof(homeplug_message::payload) - sizeof(homeplug_fragmentation_part);
|
||||
}
|
||||
}
|
||||
|
||||
void HomeplugMessage::setup_payload(void const* payload, int len, uint16_t mmtype, const defs::MMV mmv) {
|
||||
if (len > effective_payload_length(mmv)) {
|
||||
throw std::runtime_error("Homeplug Payload length too long");
|
||||
}
|
||||
raw_msg.homeplug_header.mmv = static_cast<std::underlying_type_t<defs::MMV>>(mmv);
|
||||
raw_msg.homeplug_header.mmtype = htole16(mmtype);
|
||||
|
||||
uint8_t* dst = raw_msg.payload;
|
||||
|
||||
if (mmv != defs::MMV::AV_1_0) {
|
||||
homeplug_fragmentation_part fragmentation_part{};
|
||||
fragmentation_part.fmni = 0; // not implemented
|
||||
fragmentation_part.fmsn = 0; // not implemented
|
||||
memcpy(dst, &fragmentation_part, sizeof(fragmentation_part));
|
||||
dst += sizeof(fragmentation_part); // adjust effective payload start
|
||||
}
|
||||
|
||||
// copy payload into place
|
||||
memcpy(dst, payload, len);
|
||||
|
||||
// get pointer to the end of buffer
|
||||
uint8_t* dst_end = dst + len;
|
||||
|
||||
// calculate raw message length
|
||||
raw_msg_len = dst_end - reinterpret_cast<uint8_t*>(&raw_msg);
|
||||
|
||||
// do padding
|
||||
auto padding_len = defs::MME_MIN_LENGTH - raw_msg_len;
|
||||
if (padding_len > 0) {
|
||||
memset(dst_end, 0x00, padding_len);
|
||||
raw_msg_len = defs::MME_MIN_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
void HomeplugMessage::setup_ethernet_header(const uint8_t dst_mac_addr[ETH_ALEN],
|
||||
const uint8_t src_mac_addr[ETH_ALEN]) {
|
||||
|
||||
// ethernet frame byte order is big endian
|
||||
raw_msg.ethernet_header.ether_type = htons(defs::ETH_P_HOMEPLUG_GREENPHY);
|
||||
if (dst_mac_addr) {
|
||||
memcpy(raw_msg.ethernet_header.ether_dhost, dst_mac_addr, ETH_ALEN);
|
||||
}
|
||||
|
||||
if (src_mac_addr) {
|
||||
memcpy(raw_msg.ethernet_header.ether_shost, src_mac_addr, ETH_ALEN);
|
||||
keep_src_mac = true;
|
||||
} else {
|
||||
keep_src_mac = false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t HomeplugMessage::get_mmtype() const {
|
||||
return le16toh(raw_msg.homeplug_header.mmtype);
|
||||
}
|
||||
|
||||
uint8_t* HomeplugMessage::get_src_mac() {
|
||||
return raw_msg.ethernet_header.ether_shost;
|
||||
}
|
||||
|
||||
bool HomeplugMessage::is_valid() const {
|
||||
return raw_msg_len >= defs::MME_MIN_LENGTH;
|
||||
}
|
||||
|
||||
} // namespace messages
|
||||
} // namespace slac
|
||||
Reference in New Issue
Block a user