Skip to content

Commit 321e5ed

Browse files
committed
sneaky mode refactor - hide in payload v1, don't use v2
repeater first login chacha sensing contact marking persistence channel sync V2
1 parent d189d8a commit 321e5ed

14 files changed

Lines changed: 318 additions & 187 deletions

File tree

examples/companion_radio/DataStore.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -278,13 +278,13 @@ File file = openRead(_getContactsChannelsFS(), "/contacts3");
278278
c.shared_secret_valid = false;
279279
c.supports_chacha = false;
280280
uint8_t pub_key[32];
281-
uint8_t unused;
281+
uint8_t crypto_flags; // was 'unused'; bit 0 = supports_chacha
282282

283283
bool success = (file.read(pub_key, 32) == 32);
284284
success = success && (file.read((uint8_t *)&c.name, 32) == 32);
285285
success = success && (file.read(&c.type, 1) == 1);
286286
success = success && (file.read(&c.flags, 1) == 1);
287-
success = success && (file.read(&unused, 1) == 1);
287+
success = success && (file.read(&crypto_flags, 1) == 1);
288288
success = success && (file.read((uint8_t *)&c.sync_since, 4) == 4); // was 'reserved'
289289
success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1);
290290
success = success && (file.read((uint8_t *)&c.last_advert_timestamp, 4) == 4);
@@ -295,6 +295,7 @@ File file = openRead(_getContactsChannelsFS(), "/contacts3");
295295

296296
if (!success) break; // EOF
297297

298+
c.supports_chacha = (crypto_flags & 0x01) != 0; // bit 0; old files have 0 here (backwards compatible)
298299
c.id = mesh::Identity(pub_key);
299300
if (!host->onContactLoaded(c)) full = true;
300301
}
@@ -307,14 +308,14 @@ void DataStore::saveContacts(DataStoreHost* host) {
307308
if (file) {
308309
uint32_t idx = 0;
309310
ContactInfo c;
310-
uint8_t unused = 0;
311311

312312
while (host->getContactForSave(idx, c)) {
313+
uint8_t crypto_flags = c.supports_chacha ? 0x01 : 0x00; // bit 0 = supports_chacha
313314
bool success = (file.write(c.id.pub_key, 32) == 32);
314315
success = success && (file.write((uint8_t *)&c.name, 32) == 32);
315316
success = success && (file.write(&c.type, 1) == 1);
316317
success = success && (file.write(&c.flags, 1) == 1);
317-
success = success && (file.write(&unused, 1) == 1);
318+
success = success && (file.write(&crypto_flags, 1) == 1);
318319
success = success && (file.write((uint8_t *)&c.sync_since, 4) == 4);
319320
success = success && (file.write((uint8_t *)&c.out_path_len, 1) == 1);
320321
success = success && (file.write((uint8_t *)&c.last_advert_timestamp, 4) == 4);

examples/companion_radio/MyMesh.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#define CMD_SEND_ANON_REQ 57
5757
#define CMD_SET_AUTOADD_CONFIG 58
5858
#define CMD_GET_AUTOADD_CONFIG 59
59+
#define CMD_GET_CHANNEL_V2 60 // returns 51 bytes (includes flags)
5960

6061
// Stats sub-types for CMD_GET_STATS
6162
#define STATS_TYPE_CORE 0
@@ -88,6 +89,7 @@
8889
#define RESP_CODE_TUNING_PARAMS 23
8990
#define RESP_CODE_STATS 24 // v8+, second byte is stats type
9091
#define RESP_CODE_AUTOADD_CONFIG 25
92+
#define RESP_CODE_CHANNEL_INFO_V2 26 // reply to CMD_GET_CHANNEL_V2 (51 bytes)
9193

9294
#define SEND_TIMEOUT_BASE_MILLIS 500
9395
#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f
@@ -1496,6 +1498,23 @@ void MyMesh::handleCmdFrame(size_t len) {
14961498
i += 32;
14971499
memcpy(&out_frame[i], channel.channel.secret, 16);
14981500
i += 16; // NOTE: only 128-bit supported
1501+
// flags byte NOT included - old apps expect exactly 50 bytes
1502+
_serial->writeFrame(out_frame, i);
1503+
} else {
1504+
writeErrFrame(ERR_CODE_NOT_FOUND);
1505+
}
1506+
} else if (cmd_frame[0] == CMD_GET_CHANNEL_V2 && len >= 2) {
1507+
uint8_t channel_idx = cmd_frame[1];
1508+
ChannelDetails channel;
1509+
if (getChannel(channel_idx, channel)) {
1510+
int i = 0;
1511+
out_frame[i++] = RESP_CODE_CHANNEL_INFO_V2;
1512+
out_frame[i++] = channel_idx;
1513+
strcpy((char *)&out_frame[i], channel.name);
1514+
i += 32;
1515+
memcpy(&out_frame[i], channel.channel.secret, 16);
1516+
i += 16; // NOTE: only 128-bit supported
1517+
out_frame[i++] = channel.channel.flags; // V2: includes flags byte (51 bytes total)
14991518
_serial->writeFrame(out_frame, i);
15001519
} else {
15011520
writeErrFrame(ERR_CODE_NOT_FOUND);
@@ -1505,9 +1524,13 @@ void MyMesh::handleCmdFrame(size_t len) {
15051524
} else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2 + 32 + 16) {
15061525
uint8_t channel_idx = cmd_frame[1];
15071526
ChannelDetails channel;
1527+
memset(&channel, 0, sizeof(channel));
15081528
StrHelper::strncpy(channel.name, (char *)&cmd_frame[2], 32);
1509-
memset(channel.channel.secret, 0, sizeof(channel.channel.secret));
15101529
memcpy(channel.channel.secret, &cmd_frame[2 + 32], 16); // NOTE: only 128-bit supported
1530+
// Optional flags byte: old apps don't send it (flags defaults to 0 = AES)
1531+
if (len >= 2 + 32 + 16 + 1) {
1532+
channel.channel.flags = cmd_frame[2 + 32 + 16];
1533+
}
15111534
if (setChannel(channel_idx, channel)) {
15121535
saveChannels();
15131536
writeOKFrame();

examples/simple_repeater/MyMesh.cpp

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) {
506506
}
507507

508508
void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender,
509-
uint8_t *data, size_t len) {
509+
uint8_t *data, size_t len, bool decrypted_with_chacha) {
510510
if (packet->getPayloadType() == PAYLOAD_TYPE_ANON_REQ) { // received an initial request by a possible admin
511511
// client (unknown at this stage)
512512
uint32_t timestamp;
@@ -530,17 +530,20 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m
530530

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

533-
bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
533+
// Mark peer as ChaCha-capable if we decoded their packet with ChaCha
534+
if (decrypted_with_chacha) setPeerSupportsCHACHA(&sender, -1);
535+
536+
bool use_chacha = peerSupportsCHACHA(sender);
534537
if (packet->isRouteFlood()) {
535538
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
536539
mesh::Packet* path = createPathReturn(sender, secret, packet->path, packet->path_len,
537-
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len, use_v2);
540+
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len, use_chacha);
538541
if (path) sendFlood(path, SERVER_RESPONSE_DELAY);
539542
} else if (reply_path_len < 0) {
540-
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len, use_v2);
543+
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len, use_chacha);
541544
if (reply) sendFlood(reply, SERVER_RESPONSE_DELAY);
542545
} else {
543-
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len, use_v2);
546+
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len, use_chacha);
544547
if (reply) sendDirect(reply, reply_path, reply_path_len, SERVER_RESPONSE_DELAY);
545548
}
546549
}
@@ -572,10 +575,24 @@ bool MyMesh::peerSupportsCHACHA(const mesh::Identity& dest) {
572575
if (client) {
573576
return client->supports_chacha;
574577
}
575-
// Unknown peer - default to v1 (safe fallback)
578+
// Unknown peer - default to AES (safe fallback)
576579
return false;
577580
}
578581

582+
void MyMesh::setPeerSupportsCHACHA(const mesh::Identity* id, int peer_idx) {
583+
if (peer_idx >= 0) {
584+
int i = matching_peer_indexes[peer_idx];
585+
if (i >= 0 && i < acl.getNumClients()) {
586+
acl.getClientByIdx(i)->supports_chacha = true;
587+
}
588+
} else if (id != nullptr) {
589+
ClientInfo* client = acl.getClient(id->pub_key, PUB_KEY_SIZE);
590+
if (client) {
591+
client->supports_chacha = true;
592+
}
593+
}
594+
}
595+
579596
static bool isShare(const mesh::Packet *packet) {
580597
if (packet->hasTransportCodes()) {
581598
return packet->transport_codes[0] == 0 && packet->transport_codes[1] == 0; // codes { 0, 0 } means 'send to nowhere'
@@ -624,15 +641,15 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
624641
client->last_timestamp = timestamp;
625642
client->last_activity = getRTCClock()->getCurrentTime();
626643

627-
bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
644+
bool use_chacha = client->supports_chacha;
628645
if (packet->isRouteFlood()) {
629646
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
630647
mesh::Packet *path = createPathReturn(client->id, secret, packet->path, packet->path_len,
631-
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len, use_v2);
648+
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len, use_chacha);
632649
if (path) sendFlood(path, SERVER_RESPONSE_DELAY);
633650
} else {
634651
mesh::Packet *reply =
635-
createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len, use_v2);
652+
createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len, use_chacha);
636653
if (reply) {
637654
if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT
638655
sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY);
@@ -693,8 +710,8 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
693710
memcpy(temp, &timestamp, 4); // mostly an extra blob to help make packet_hash unique
694711
temp[4] = (TXT_TYPE_CLI_DATA << 2); // NOTE: legacy was: TXT_TYPE_PLAIN
695712

696-
bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
697-
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len, use_v2);
713+
bool use_chacha = client->supports_chacha;
714+
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len, use_chacha);
698715
if (reply) {
699716
if (client->out_path_len < 0) {
700717
sendFlood(reply, CLI_REPLY_DELAY_MILLIS);

examples/simple_repeater/MyMesh.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,11 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
159159

160160
bool filterRecvFloodPacket(mesh::Packet* pkt) override;
161161

162-
void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override;
162+
void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len, bool decrypted_with_chacha = false) override;
163163
int searchPeersByHash(const uint8_t* hash) override;
164164
void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override;
165165
bool peerSupportsCHACHA(const mesh::Identity& dest) override;
166+
void setPeerSupportsCHACHA(const mesh::Identity* id, int peer_idx) override;
166167
void onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len) override;
167168
void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override;
168169
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;

examples/simple_room_server/MyMesh.cpp

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ void MyMesh::pushPostToClient(ClientInfo *client, PostInfo &post) {
7171
mesh::Utils::sha256((uint8_t *)&client->extra.room.pending_ack, 4, reply_data, len, client->id.pub_key, PUB_KEY_SIZE);
7272
client->extra.room.push_post_timestamp = post.post_timestamp;
7373

74-
bool use_v2 = client->supports_chacha;
75-
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, client->shared_secret, reply_data, len, use_v2);
74+
bool use_chacha = client->supports_chacha;
75+
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, client->shared_secret, reply_data, len, use_chacha);
7676
if (reply) {
7777
if (client->out_path_len < 0) {
7878
sendFlood(reply);
@@ -280,7 +280,7 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
280280
}
281281

282282
void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender,
283-
uint8_t *data, size_t len) {
283+
uint8_t *data, size_t len, bool decrypted_with_chacha) {
284284
if (packet->getPayloadType() == PAYLOAD_TYPE_ANON_REQ) { // received an initial request by a possible admin
285285
// client (unknown at this stage)
286286
uint32_t sender_timestamp, sender_sync_since;
@@ -350,14 +350,17 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m
350350

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

353-
bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
353+
// Mark peer as ChaCha-capable if we decoded their packet with ChaCha
354+
if (decrypted_with_chacha) client->supports_chacha = true;
355+
356+
bool use_chacha = client->supports_chacha;
354357
if (packet->isRouteFlood()) {
355358
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
356359
mesh::Packet *path = createPathReturn(sender, client->shared_secret, packet->path, packet->path_len,
357-
PAYLOAD_TYPE_RESPONSE, reply_data, 13, use_v2);
360+
PAYLOAD_TYPE_RESPONSE, reply_data, 13, use_chacha);
358361
if (path) sendFlood(path, SERVER_RESPONSE_DELAY);
359362
} else {
360-
mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, client->shared_secret, reply_data, 13, use_v2);
363+
mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, client->shared_secret, reply_data, 13, use_chacha);
361364
if (reply) {
362365
if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT
363366
sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY);
@@ -395,10 +398,24 @@ bool MyMesh::peerSupportsCHACHA(const mesh::Identity& dest) {
395398
if (client) {
396399
return client->supports_chacha;
397400
}
398-
// Unknown peer - default to v1 (safe fallback)
401+
// Unknown peer - default to AES (safe fallback)
399402
return false;
400403
}
401404

405+
void MyMesh::setPeerSupportsCHACHA(const mesh::Identity* id, int peer_idx) {
406+
if (peer_idx >= 0) {
407+
int i = matching_peer_indexes[peer_idx];
408+
if (i >= 0 && i < acl.getNumClients()) {
409+
acl.getClientByIdx(i)->supports_chacha = true;
410+
}
411+
} else if (id != nullptr) {
412+
ClientInfo* client = acl.getClient(id->pub_key, PUB_KEY_SIZE);
413+
if (client) {
414+
client->supports_chacha = true;
415+
}
416+
}
417+
}
418+
402419
void MyMesh::onAdvertRecv(mesh::Packet *packet, const mesh::Identity &id, uint32_t timestamp,
403420
const uint8_t *app_data, size_t app_data_len) {
404421
mesh::Mesh::onAdvertRecv(packet, id, timestamp, app_data, app_data_len); // chain to super impl
@@ -506,8 +523,8 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
506523
// mesh::Utils::sha256((uint8_t *)&expected_ack_crc, 4, temp, 5 + text_len, self_id.pub_key,
507524
// PUB_KEY_SIZE);
508525

509-
bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
510-
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len, use_v2);
526+
bool use_chacha = client->supports_chacha;
527+
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len, use_chacha);
511528
if (reply) {
512529
if (client->out_path_len < 0) {
513530
sendFlood(reply, delay_millis + SERVER_RESPONSE_DELAY);
@@ -561,14 +578,14 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
561578
} else {
562579
int reply_len = handleRequest(client, sender_timestamp, &data[4], len - 4);
563580
if (reply_len > 0) { // valid command
564-
bool use_v2 = (packet->getPayloadVer() == PAYLOAD_VER_2);
581+
bool use_chacha = client->supports_chacha;
565582
if (packet->isRouteFlood()) {
566583
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
567584
mesh::Packet *path = createPathReturn(client->id, secret, packet->path, packet->path_len,
568-
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len, use_v2);
585+
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len, use_chacha);
569586
if (path) sendFlood(path, SERVER_RESPONSE_DELAY);
570587
} else {
571-
mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len, use_v2);
588+
mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len, use_chacha);
572589
if (reply) {
573590
if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT
574591
sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY);

examples/simple_room_server/MyMesh.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,11 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
145145
}
146146

147147
bool allowPacketForward(const mesh::Packet* packet) override;
148-
void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override;
148+
void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len, bool decrypted_with_chacha = false) override;
149149
int searchPeersByHash(const uint8_t* hash) override ;
150150
void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override;
151151
bool peerSupportsCHACHA(const mesh::Identity& dest) override;
152+
void setPeerSupportsCHACHA(const mesh::Identity* id, int peer_idx) override;
152153
void onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len) override;
153154
void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override;
154155
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;

0 commit comments

Comments
 (0)