Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions examples/companion_radio/DataStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ File file = openRead(_getContactsChannelsFS(), "/contacts3");
bool full = false;
while (!full) {
ContactInfo c;
memset(&c, 0, sizeof(c)); // Zero-initialize to prevent garbage in new fields
c.shared_secret_valid = false;
c.supports_chacha = false;
uint8_t pub_key[32];
uint8_t unused;

Expand Down Expand Up @@ -335,14 +338,18 @@ void DataStore::loadChannels(DataStoreHost* host) {
uint8_t channel_idx = 0;
while (!full) {
ChannelDetails ch;
uint8_t unused[4];
uint8_t flags_and_unused[4];

bool success = (file.read(unused, 4) == 4);
bool success = (file.read(flags_and_unused, 4) == 4);
success = success && (file.read((uint8_t *)ch.name, 32) == 32);
success = success && (file.read((uint8_t *)ch.channel.secret, 32) == 32);

if (!success) break; // EOF

// First byte stores channel flags (v2 support, etc), rest unused
// Old files have all zeros here, which means v1 (backward compatible)
ch.channel.flags = flags_and_unused[0];

if (host->onChannelLoaded(channel_idx, ch)) {
channel_idx++;
} else {
Expand All @@ -358,11 +365,14 @@ void DataStore::saveChannels(DataStoreHost* host) {
if (file) {
uint8_t channel_idx = 0;
ChannelDetails ch;
uint8_t unused[4];
memset(unused, 0, 4);
uint8_t flags_and_unused[4];

while (host->getChannelForSave(channel_idx, ch)) {
bool success = (file.write(unused, 4) == 4);
// First byte stores channel flags (v2 support, etc), rest unused
memset(flags_and_unused, 0, 4);
flags_and_unused[0] = ch.channel.flags;

bool success = (file.write(flags_and_unused, 4) == 4);
success = success && (file.write((uint8_t *)ch.name, 32) == 32);
success = success && (file.write((uint8_t *)ch.channel.secret, 32) == 32);

Expand Down
43 changes: 32 additions & 11 deletions examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,16 +530,17 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m

if (reply_len == 0) return; // invalid request

bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
if (packet->isRouteFlood()) {
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
mesh::Packet* path = createPathReturn(sender, secret, packet->path, packet->path_len,
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len);
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len, use_v2);
if (path) sendFlood(path, SERVER_RESPONSE_DELAY);
} else if (reply_path_len < 0) {
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len);
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len, use_v2);
if (reply) sendFlood(reply, SERVER_RESPONSE_DELAY);
} else {
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len);
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len, use_v2);
if (reply) sendDirect(reply, reply_path, reply_path_len, SERVER_RESPONSE_DELAY);
}
}
Expand All @@ -565,6 +566,16 @@ void MyMesh::getPeerSharedSecret(uint8_t *dest_secret, int peer_idx) {
}
}

bool MyMesh::peerSupportsCHACHA(const mesh::Identity& dest) {
// Look up client by public key
ClientInfo* client = acl.getClient(dest.pub_key, PUB_KEY_SIZE);
if (client) {
return client->supports_chacha;
}
// Unknown peer - default to v1 (safe fallback)
return false;
}

static bool isShare(const mesh::Packet *packet) {
if (packet->hasTransportCodes()) {
return packet->transport_codes[0] == 0 && packet->transport_codes[1] == 0; // codes { 0, 0 } means 'send to nowhere'
Expand All @@ -576,11 +587,19 @@ void MyMesh::onAdvertRecv(mesh::Packet *packet, const mesh::Identity &id, uint32
const uint8_t *app_data, size_t app_data_len) {
mesh::Mesh::onAdvertRecv(packet, id, timestamp, app_data, app_data_len); // chain to super impl

// if this a zero hop advert (and not via 'Share'), add it to neighbours
if (packet->path_len == 0 && !isShare(packet)) {
AdvertDataParser parser(app_data, app_data_len);
if (parser.isValid() && parser.getType() == ADV_TYPE_REPEATER) { // just keep neigbouring Repeaters
putNeighbour(id, timestamp, packet->getSNR());
AdvertDataParser parser(app_data, app_data_len);
if (parser.isValid()) {
// Update CHACHA capability for known clients (chat nodes that are in our ACL)
ClientInfo* client = acl.getClient(id.pub_key, PUB_KEY_SIZE);
if (client) {
client->supports_chacha = (parser.getFeat1() & ADV_FEAT1_CHACHA_CAPABLE) != 0;
}

// if this a zero hop advert (and not via 'Share'), add it to neighbours
if (packet->path_len == 0 && !isShare(packet)) {
if (parser.getType() == ADV_TYPE_REPEATER) { // just keep neigbouring Repeaters
putNeighbour(id, timestamp, packet->getSNR());
}
}
}
}
Expand All @@ -605,14 +624,15 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
client->last_timestamp = timestamp;
client->last_activity = getRTCClock()->getCurrentTime();

bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
if (packet->isRouteFlood()) {
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
mesh::Packet *path = createPathReturn(client->id, secret, packet->path, packet->path_len,
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len);
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len, use_v2);
if (path) sendFlood(path, SERVER_RESPONSE_DELAY);
} else {
mesh::Packet *reply =
createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len);
createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len, use_v2);
if (reply) {
if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT
sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY);
Expand Down Expand Up @@ -673,7 +693,8 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
memcpy(temp, &timestamp, 4); // mostly an extra blob to help make packet_hash unique
temp[4] = (TXT_TYPE_CLI_DATA << 2); // NOTE: legacy was: TXT_TYPE_PLAIN

auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len);
bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len, use_v2);
if (reply) {
if (client->out_path_len < 0) {
sendFlood(reply, CLI_REPLY_DELAY_MILLIS);
Expand Down
3 changes: 2 additions & 1 deletion examples/simple_repeater/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override;
int searchPeersByHash(const uint8_t* hash) override;
void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override;
void onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len);
bool peerSupportsCHACHA(const mesh::Identity& dest) override;
void onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len) override;
void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override;
bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override;
void onControlDataRecv(mesh::Packet* packet) override;
Expand Down
40 changes: 34 additions & 6 deletions examples/simple_room_server/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ void MyMesh::pushPostToClient(ClientInfo *client, PostInfo &post) {
mesh::Utils::sha256((uint8_t *)&client->extra.room.pending_ack, 4, reply_data, len, client->id.pub_key, PUB_KEY_SIZE);
client->extra.room.push_post_timestamp = post.post_timestamp;

auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, client->shared_secret, reply_data, len);
bool use_v2 = client->supports_chacha;
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, client->shared_secret, reply_data, len, use_v2);
if (reply) {
if (client->out_path_len < 0) {
sendFlood(reply);
Expand Down Expand Up @@ -349,13 +350,14 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m

next_push = futureMillis(PUSH_NOTIFY_DELAY_MILLIS); // delay next push, give RESPONSE packet time to arrive first

bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
if (packet->isRouteFlood()) {
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
mesh::Packet *path = createPathReturn(sender, client->shared_secret, packet->path, packet->path_len,
PAYLOAD_TYPE_RESPONSE, reply_data, 13);
PAYLOAD_TYPE_RESPONSE, reply_data, 13, use_v2);
if (path) sendFlood(path, SERVER_RESPONSE_DELAY);
} else {
mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, client->shared_secret, reply_data, 13);
mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, client->shared_secret, reply_data, 13, use_v2);
if (reply) {
if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT
sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY);
Expand Down Expand Up @@ -387,6 +389,30 @@ void MyMesh::getPeerSharedSecret(uint8_t *dest_secret, int peer_idx) {
}
}

bool MyMesh::peerSupportsCHACHA(const mesh::Identity& dest) {
// Look up client by public key
ClientInfo* client = acl.getClient(dest.pub_key, PUB_KEY_SIZE);
if (client) {
return client->supports_chacha;
}
// Unknown peer - default to v1 (safe fallback)
return false;
}

void MyMesh::onAdvertRecv(mesh::Packet *packet, const mesh::Identity &id, uint32_t timestamp,
const uint8_t *app_data, size_t app_data_len) {
mesh::Mesh::onAdvertRecv(packet, id, timestamp, app_data, app_data_len); // chain to super impl

// Update CHACHA capability for known clients
AdvertDataParser parser(app_data, app_data_len);
if (parser.isValid()) {
ClientInfo* client = acl.getClient(id.pub_key, PUB_KEY_SIZE);
if (client) {
client->supports_chacha = (parser.getFeat1() & ADV_FEAT1_CHACHA_CAPABLE) != 0;
}
}
}

void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, const uint8_t *secret,
uint8_t *data, size_t len) {
int i = matching_peer_indexes[sender_idx];
Expand Down Expand Up @@ -480,7 +506,8 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
// mesh::Utils::sha256((uint8_t *)&expected_ack_crc, 4, temp, 5 + text_len, self_id.pub_key,
// PUB_KEY_SIZE);

auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len);
bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len, use_v2);
if (reply) {
if (client->out_path_len < 0) {
sendFlood(reply, delay_millis + SERVER_RESPONSE_DELAY);
Expand Down Expand Up @@ -534,13 +561,14 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
} else {
int reply_len = handleRequest(client, sender_timestamp, &data[4], len - 4);
if (reply_len > 0) { // valid command
bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
if (packet->isRouteFlood()) {
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
mesh::Packet *path = createPathReturn(client->id, secret, packet->path, packet->path_len,
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len);
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len, use_v2);
if (path) sendFlood(path, SERVER_RESPONSE_DELAY);
} else {
mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len);
mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len, use_v2);
if (reply) {
if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT
sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY);
Expand Down
2 changes: 2 additions & 0 deletions examples/simple_room_server/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override;
int searchPeersByHash(const uint8_t* hash) override ;
void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override;
bool peerSupportsCHACHA(const mesh::Identity& dest) override;
void onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len) override;
void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override;
bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override;
void onAckRecv(mesh::Packet* packet, uint32_t ack_crc) override;
Expand Down
45 changes: 37 additions & 8 deletions examples/simple_sensor/SensorMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ void SensorMesh::sendAlert(const ClientInfo* c, Trigger* t) {
mesh::Utils::sha256((uint8_t *)&t->expected_acks[t->attempt], 4, data, 5 + text_len, self_id.pub_key, PUB_KEY_SIZE);
t->attempt++;

auto pkt = createDatagram(PAYLOAD_TYPE_TXT_MSG, c->id, c->shared_secret, data, 5 + text_len);
bool use_v2 = c->supports_chacha;
auto pkt = createDatagram(PAYLOAD_TYPE_TXT_MSG, c->id, c->shared_secret, data, 5 + text_len, use_v2);
if (pkt) {
if (c->out_path_len >= 0) { // we have an out_path, so send DIRECT
sendDirect(pkt, c->out_path, c->out_path_len);
Expand Down Expand Up @@ -464,13 +465,14 @@ void SensorMesh::onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, con

if (reply_len == 0) return; // invalid request

bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
if (packet->isRouteFlood()) {
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
mesh::Packet* path = createPathReturn(sender, secret, packet->path, packet->path_len,
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len);
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len, use_v2);
if (path) sendFlood(path, SERVER_RESPONSE_DELAY);
} else {
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len);
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len, use_v2);
if (reply) sendFlood(reply, SERVER_RESPONSE_DELAY);
}
}
Expand All @@ -496,6 +498,30 @@ void SensorMesh::getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) {
}
}

bool SensorMesh::peerSupportsCHACHA(const mesh::Identity& dest) {
// Look up client by public key
ClientInfo* client = acl.getClient(dest.pub_key, PUB_KEY_SIZE);
if (client) {
return client->supports_chacha;
}
// Unknown peer - default to v1 (safe fallback)
return false;
}

void SensorMesh::onAdvertRecv(mesh::Packet *packet, const mesh::Identity &id, uint32_t timestamp,
const uint8_t *app_data, size_t app_data_len) {
mesh::Mesh::onAdvertRecv(packet, id, timestamp, app_data, app_data_len); // chain to super impl

// Update CHACHA capability for known clients
AdvertDataParser parser(app_data, app_data_len);
if (parser.isValid()) {
ClientInfo* client = acl.getClient(id.pub_key, PUB_KEY_SIZE);
if (client) {
client->supports_chacha = (parser.getFeat1() & ADV_FEAT1_CHACHA_CAPABLE) != 0;
}
}
}

void SensorMesh::sendAckTo(const ClientInfo& dest, uint32_t ack_hash) {
if (dest.out_path_len < 0) {
mesh::Packet* ack = createAck(ack_hash);
Expand Down Expand Up @@ -533,13 +559,14 @@ void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_i
from->last_timestamp = timestamp;
from->last_activity = getRTCClock()->getCurrentTime();

bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
if (packet->isRouteFlood()) {
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
mesh::Packet* path = createPathReturn(from->id, secret, packet->path, packet->path_len,
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len);
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len, use_v2);
if (path) sendFlood(path, SERVER_RESPONSE_DELAY);
} else {
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, from->id, secret, reply_data, reply_len);
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, from->id, secret, reply_data, reply_len, use_v2);
if (reply) {
if (from->out_path_len >= 0) { // we have an out_path, so send DIRECT
sendDirect(reply, from->out_path, from->out_path_len, SERVER_RESPONSE_DELAY);
Expand All @@ -565,14 +592,15 @@ void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_i

if (packet->isRouteFlood()) {
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the ACK
bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
mesh::Packet* path = createPathReturn(from->id, secret, packet->path, packet->path_len,
PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4);
PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4, use_v2);
if (path) sendFlood(path, TXT_ACK_DELAY);
} else {
sendAckTo(*from, ack_hash);
}
}
} else if (flags == TXT_TYPE_CLI_DATA) {
} else if (flags == TXT_TYPE_CLI_DATA) {
from->last_timestamp = sender_timestamp;
from->last_activity = getRTCClock()->getCurrentTime();

Expand All @@ -594,7 +622,8 @@ void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_i
memcpy(temp, &timestamp, 4); // mostly an extra blob to help make packet_hash unique
temp[4] = (TXT_TYPE_CLI_DATA << 2);

auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, from->id, secret, temp, 5 + text_len);
bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, from->id, secret, temp, 5 + text_len, use_v2);
if (reply) {
if (from->out_path_len < 0) {
sendFlood(reply, CLI_REPLY_DELAY_MILLIS);
Expand Down
Loading