still wip
This commit is contained in:
@@ -1,547 +1,74 @@
|
||||
#include "meshcore.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "lib/monocypher/monocypher-ed25519.h"
|
||||
#include "lib/ed25519/ed_25519.h"
|
||||
#include "meshcore/packets/advert.h"
|
||||
#include "meshcore/packets/anonymous.h"
|
||||
#include "meshcore/packets/control.h"
|
||||
#include "meshcore/packets/encrypted.h"
|
||||
#include "meshcore/packets/group.h"
|
||||
#include "task.h"
|
||||
#include "lib/base64.h"
|
||||
#include "lib/cifra/aes.h"
|
||||
#include "lib/cifra/sha2.h"
|
||||
#include "lib/cifra/hmac.h"
|
||||
#include "lib/config.h"
|
||||
|
||||
#define AESKeyCount 8
|
||||
|
||||
const uint8_t aesKeys[AESKeyCount][17] = {
|
||||
{0x11, 0x8b, 0x33, 0x87, 0xe9, 0xc5,
|
||||
0xcd, 0xea, 0x6a, 0xc9, 0xe5, 0xed,
|
||||
0xba, 0xa1, 0x15, 0xcd, 0x72},
|
||||
|
||||
{0x0a, 0x44, 0x81, 0xda, 0x0e, 0x4e,
|
||||
0x03, 0xc4, 0x9e, 0x84, 0x77, 0x25,
|
||||
0xd8, 0x3a, 0x93, 0xbf, 0x80}
|
||||
};
|
||||
#include "meshframing.h"
|
||||
|
||||
#define TAG "MeshCore"
|
||||
|
||||
// requires at least a 256 byte data
|
||||
FrameStruct decodeFrame (unsigned char *data, unsigned char dataLen) {
|
||||
hexdump ("RxDump", data, dataLen);
|
||||
FrameStruct frame;
|
||||
memset (&frame, 0, sizeof (frame));
|
||||
unsigned char index = 0;
|
||||
frame.header = data[index++];
|
||||
if ((frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT ||
|
||||
(frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
memcpy (frame.transportCodes, data + index, 4);
|
||||
index += 4;
|
||||
}
|
||||
frame.pathLen = data[index++];
|
||||
|
||||
memcpy (frame.path, data + index, frame.pathLen);
|
||||
index += frame.pathLen;
|
||||
frame.payloadLen = dataLen - index;
|
||||
memcpy (frame.payload, data + index, frame.payloadLen);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
int aes_encrypt_ecb (const uint8_t *key, const uint8_t *input, size_t ilen,
|
||||
uint8_t *output) {
|
||||
if (ilen % 16 != 0)
|
||||
return -1;
|
||||
|
||||
cf_aes_context ctx;
|
||||
cf_aes_init (&ctx, key, 16);
|
||||
|
||||
for (size_t i = 0; i < ilen; i += 16) {
|
||||
cf_aes_encrypt (&ctx, input + i, output + i);
|
||||
void processFrame (FrameStruct frame) {
|
||||
printFrameHeader (frame);
|
||||
if (frame.header & PAYLOAD_VERSION_3) { // more than the version 0
|
||||
ESP_LOGW (TAG, "Frame too new, got version %d instead of 0", (frame.header & PAYLOAD_VERSION_3) >> 6);
|
||||
}
|
||||
|
||||
cf_aes_finish (&ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aes_decrypt_ecb (const uint8_t *key, const uint8_t *input, size_t ilen,
|
||||
uint8_t *output) {
|
||||
if (ilen % 16 != 0)
|
||||
return -1;
|
||||
|
||||
cf_aes_context ctx;
|
||||
cf_aes_init (&ctx, key, 16);
|
||||
|
||||
for (size_t i = 0; i < ilen; i += 16) {
|
||||
cf_aes_decrypt (&ctx, input + i, output + i);
|
||||
}
|
||||
|
||||
|
||||
cf_aes_finish (&ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// HMAC-SHA256
|
||||
int hmac_sha256 (const uint8_t *key, size_t keylen,
|
||||
const uint8_t *input, size_t ilen,
|
||||
uint8_t *output) {
|
||||
cf_hmac_ctx ctx;
|
||||
cf_hmac_init (&ctx, &cf_sha256, key, keylen);
|
||||
cf_hmac_update (&ctx, input, ilen);
|
||||
cf_hmac_finish (&ctx, output);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Verify MAC + Decrypt
|
||||
#define KEY_SIZE 16 // AES-128
|
||||
|
||||
int encrypt_then_mac (const uint8_t *aes_key,
|
||||
const uint8_t *plaintext, size_t plen,
|
||||
uint8_t *output, size_t *olen) {
|
||||
if (plen == 0)
|
||||
return -1;
|
||||
|
||||
// ciphertext will go right after HMAC
|
||||
uint8_t *ciphertext = output + HMAC_SIZE;
|
||||
|
||||
// encrypt plaintext
|
||||
aes_encrypt_ecb (aes_key, plaintext, plen, ciphertext);
|
||||
|
||||
// compute HMAC over ciphertext
|
||||
uint8_t mac[32]; // full SHA-256
|
||||
hmac_sha256 (aes_key, KEY_SIZE, ciphertext, plen, mac);
|
||||
|
||||
// copy only HMAC_SIZE bytes of MAC
|
||||
memcpy (output, mac, HMAC_SIZE);
|
||||
|
||||
// return total length = HMAC + ciphertext
|
||||
*olen = HMAC_SIZE + plen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_then_decrypt (const uint8_t *aes_key, const uint8_t *input, size_t ilen,
|
||||
uint8_t *plaintext) {
|
||||
if (ilen <= HMAC_SIZE)
|
||||
return -1;
|
||||
|
||||
const uint8_t *mac = input;
|
||||
const uint8_t *ciphertext = input + HMAC_SIZE;
|
||||
size_t clen = ilen - HMAC_SIZE;
|
||||
|
||||
uint8_t calc_mac[32]; // full SHA-256
|
||||
hmac_sha256 (aes_key, KEY_SIZE, ciphertext, clen, calc_mac);
|
||||
|
||||
if (memcmp (mac, calc_mac, HMAC_SIZE) != 0)
|
||||
return -2;
|
||||
|
||||
return aes_decrypt_ecb (aes_key, ciphertext, clen, plaintext);
|
||||
}
|
||||
|
||||
void hexdump (const char *label, const uint8_t *data, size_t len) {
|
||||
if (label)
|
||||
printf ("%s (len=%zu):\n", label, len);
|
||||
|
||||
for (size_t i = 0; i < len; i += 16) {
|
||||
printf ("%04zx ", i); // offset
|
||||
for (size_t j = 0; j < 16; j++) {
|
||||
if (i + j < len)
|
||||
printf ("%02X ", data[i + j]);
|
||||
else
|
||||
printf (" "); // pad spacing
|
||||
}
|
||||
printf (" ");
|
||||
for (size_t j = 0; j < 16 && i + j < len; j++) {
|
||||
uint8_t c = data[i + j];
|
||||
printf ("%c", isprint (c) ? c : '.');
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// EncryptedPayloadStruct decodeEncryptedPayload(FrameStruct frame) {
|
||||
// EncryptedPayloadStruct enc;
|
||||
// memset(&enc, 0, sizeof(enc));
|
||||
// if ((frame.header & PAYLOAD_TYPE_MASK) != PAYLOAD_TYPE_GRP_TXT) {
|
||||
// return enc;
|
||||
// }
|
||||
// unsigned char index = 0;
|
||||
// enc.destinationHash = frame.payload[index++];
|
||||
// enc.sourceHash = frame.payload[index++];
|
||||
|
||||
|
||||
// index = 0;
|
||||
|
||||
// memcpy(&msg.timestamp, tmp + index, 4);
|
||||
// index += 4;
|
||||
// msg.flags = tmp[index++];
|
||||
|
||||
// memcpy(msg.text, tmp + index, plaintextLen - index);
|
||||
// return end;
|
||||
// }
|
||||
|
||||
GroupTextMessage decodeGroupMessage (FrameStruct frame) {
|
||||
GroupTextMessage msg;
|
||||
memset (&msg, 0, sizeof (msg));
|
||||
if ((frame.header & PAYLOAD_TYPE_MASK) != PAYLOAD_TYPE_GRP_TXT) {
|
||||
ESP_LOGW (TAG, "Not a group text");
|
||||
return msg;
|
||||
}
|
||||
unsigned char index = 0;
|
||||
msg.channelHash = frame.payload[index++];
|
||||
unsigned char tmp[184];
|
||||
|
||||
|
||||
unsigned char decrypted = 0;
|
||||
for (unsigned char i = 0; i < AESKeyCount; i++) {
|
||||
|
||||
if (msg.channelHash != aesKeys[i][0]) {
|
||||
ESP_LOGW (TAG, "Hash %d does not equal %d", aesKeys[i][0], msg.channelHash);
|
||||
continue;
|
||||
}
|
||||
|
||||
ESP_LOGW (TAG, "Hash does equal %d", msg.channelHash);
|
||||
|
||||
if (mac_then_decrypt (aesKeys[i] + 1, frame.payload + index, frame.payloadLen - index, tmp) != 0) {
|
||||
ESP_LOGW (TAG, "HMAC failed on grouphash key %d not matching %d", aesKeys[i][0], msg.channelHash);
|
||||
continue;
|
||||
}
|
||||
hexdump ("RxDumpDec", tmp, frame.payloadLen - index);
|
||||
decrypted = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!decrypted) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
unsigned char plaintextLen = frame.payloadLen - index;
|
||||
index = 0;
|
||||
|
||||
ESP_LOGI (TAG, "Starting memcpy");
|
||||
vTaskDelay (pdMS_TO_TICKS (10));
|
||||
memcpy (&msg.timestamp, tmp + index, 4);
|
||||
index += 4;
|
||||
msg.flags = tmp[index++];
|
||||
|
||||
memcpy (msg.text, tmp + index, plaintextLen - index);
|
||||
return msg;
|
||||
}
|
||||
|
||||
void printGroupMessage (GroupTextMessage msg) {
|
||||
printf ("Message with channel hash %d, flags %d: %s\n", msg.channelHash,
|
||||
msg.flags, msg.text);
|
||||
}
|
||||
|
||||
AdvertisementPayload decodeAdvertisement (FrameStruct frame) {
|
||||
unsigned char checkSumBuf[10];
|
||||
AdvertisementPayload advert;
|
||||
memset (&advert, 0, sizeof (advert));
|
||||
GroupTextMessage msg;
|
||||
|
||||
if ((frame.header & PAYLOAD_TYPE_MASK) != PAYLOAD_TYPE_ADVERT) {
|
||||
return advert;
|
||||
}
|
||||
unsigned char frameType = frame.header & PAYLOAD_TYPE_MASK;
|
||||
|
||||
unsigned char index = 0;
|
||||
|
||||
memcpy (advert.pubKey, frame.payload + index, 32);
|
||||
index += 32;
|
||||
if (frameType == PAYLOAD_TYPE_ANON_REQ) {
|
||||
decodeAnonReq (frame);
|
||||
|
||||
memcpy (&advert.timestamp, frame.payload + index, 4);
|
||||
index += 4;
|
||||
} else if (frameType == PAYLOAD_TYPE_PATH || frameType == PAYLOAD_TYPE_REQ || frameType == PAYLOAD_TYPE_RESPONSE || frameType == PAYLOAD_TYPE_TXT_MSG) {
|
||||
printf (" Typexd: 0x%02X\n", frameType);
|
||||
EncryptedPayloadStruct enc = decodeEncryptedPayload (frame);
|
||||
printf (" Typexdd: 0x%02X\n", enc.type);
|
||||
if (enc.payloadLen > 0) {
|
||||
parseEncryptedPayload (enc);
|
||||
}
|
||||
} else if (frameType == PAYLOAD_TYPE_ACK) {
|
||||
memset (checkSumBuf, 0, sizeof (checkSumBuf));
|
||||
base64_encode (frame.payload, 4, checkSumBuf);
|
||||
printf ("Checksum: %s\n", checkSumBuf);
|
||||
} else if (frameType == PAYLOAD_TYPE_ACK) {
|
||||
uint32_t checkSum = frame.payload[index++];
|
||||
checkSum |= frame.payload[index++] << 8;
|
||||
checkSum |= frame.payload[index++] << 16;
|
||||
checkSum |= frame.payload[index++] << 24;
|
||||
|
||||
memcpy (advert.signature, frame.payload + index, 64);
|
||||
index += 64;
|
||||
} else if (frameType == PAYLOAD_TYPE_ADVERT) {
|
||||
advert = decodeAdvertisement (frame);
|
||||
printAdvertisement (advert);
|
||||
} else if (frameType == PAYLOAD_TYPE_GRP_TXT || frameType == PAYLOAD_TYPE_GRP_DATA) {
|
||||
msg = decodeGroupMessage (frame);
|
||||
printGroupMessage (msg);
|
||||
} else if (frameType == PAYLOAD_TYPE_TRACE) {
|
||||
|
||||
advert.dataFlags = frame.payload[index++];
|
||||
} else if (frameType == PAYLOAD_TYPE_MULTIPART) {
|
||||
|
||||
if (advert.dataFlags & ADVERTISEMENT_FLAG_HAS_LOCATION) {
|
||||
memcpy (&advert.latitude, frame.payload + index, 4);
|
||||
index += 4;
|
||||
memcpy (&advert.longitude, frame.payload + index, 4);
|
||||
index += 4;
|
||||
} else if (frameType == PAYLOAD_TYPE_CONTROL) {
|
||||
decodeControlFrame (frame);
|
||||
|
||||
} else if (frameType == PAYLOAD_TYPE_RAW_CUSTOM) {
|
||||
// not implemented
|
||||
}
|
||||
|
||||
if (advert.dataFlags & ADVERTISEMENT_FLAG_RFU1) {
|
||||
memcpy (&advert.rfu1, frame.payload + index, 2);
|
||||
index += 2;
|
||||
}
|
||||
if (advert.dataFlags & ADVERTISEMENT_FLAG_RFU2) {
|
||||
memcpy (&advert.rfu2, frame.payload + index, 2);
|
||||
index += 2;
|
||||
}
|
||||
memcpy (advert.nodeName, frame.payload + index, frame.payloadLen - index);
|
||||
advert.nodeName[frame.payloadLen - index] = 0;
|
||||
|
||||
return advert;
|
||||
}
|
||||
|
||||
void printAdvertisement (AdvertisementPayload advert) {
|
||||
unsigned char keyBuf[50];
|
||||
unsigned char sigBuf[90];
|
||||
memset (keyBuf, 0, sizeof (keyBuf));
|
||||
memset (sigBuf, 0, sizeof (sigBuf));
|
||||
base64_encode (advert.pubKey, 32, keyBuf);
|
||||
base64_encode (advert.signature, 64, sigBuf);
|
||||
|
||||
printf ("%s on %ld with type %s on %s location %ld %ld, public key %s and "
|
||||
"signature %s\n",
|
||||
advert.dataFlags & ADVERTISEMENT_FLAG_HAS_NAME ? advert.nodeName
|
||||
: "nameless node",
|
||||
advert.timestamp,
|
||||
(advert.dataFlags & 0x07) == 0x04
|
||||
? "sensor"
|
||||
: ((advert.dataFlags & 0x07) == 0x03
|
||||
? "room server"
|
||||
: ((advert.dataFlags & 0x07) == 0x02 ? "repeater"
|
||||
: "chat node")),
|
||||
advert.dataFlags & 0x80 ? "known" : "unknown", advert.latitude,
|
||||
advert.longitude, keyBuf, sigBuf);
|
||||
}
|
||||
|
||||
void printFrameHeader (FrameStruct frame) {
|
||||
switch (frame.header & ROUTE_TYPE_MASK) {
|
||||
case ROUTE_TYPE_TRANSPORT_FLOOD:
|
||||
printf ("transport flood");
|
||||
break;
|
||||
|
||||
case ROUTE_TYPE_FLOOD:
|
||||
printf ("flood");
|
||||
break;
|
||||
|
||||
case ROUTE_TYPE_DIRECT:
|
||||
printf ("direct");
|
||||
break;
|
||||
|
||||
case ROUTE_TYPE_TRANSPORT_DIRECT:
|
||||
printf ("transport direct");
|
||||
break;
|
||||
}
|
||||
|
||||
printf (", payload type is ");
|
||||
|
||||
switch (frame.header & PAYLOAD_TYPE_MASK) {
|
||||
case PAYLOAD_TYPE_REQ:
|
||||
printf ("request");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_RESPONSE:
|
||||
printf ("response");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_TXT_MSG:
|
||||
printf ("text message");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_ACK:
|
||||
printf ("acknowledgement");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_ADVERT:
|
||||
printf ("advert");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_GRP_TXT:
|
||||
printf ("group text");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_GRP_DATA:
|
||||
printf ("group data");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_ANON_REQ:
|
||||
printf ("anon request");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_PATH:
|
||||
printf ("path");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_TRACE:
|
||||
printf ("trace");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_MULTIPART:
|
||||
printf ("multipart");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_RAW_CUSTOM:
|
||||
printf ("raw");
|
||||
break;
|
||||
}
|
||||
char version[2];
|
||||
version[0] = (frame.header >> 6) + '0';
|
||||
version[1] = 0;
|
||||
|
||||
printf (", payload version is %s ", version);
|
||||
|
||||
if ((frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT ||
|
||||
(frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
printf ("Transport codes: %d %d\n", *((uint16_t *)frame.transportCodes),
|
||||
*((uint16_t *)&(frame.transportCodes[2])));
|
||||
}
|
||||
printf ("Path is %d nodes long", frame.pathLen);
|
||||
|
||||
for (uint8_t pathIndex = 0; pathIndex < frame.pathLen; pathIndex++) {
|
||||
printf ("node %d - %02X, ", pathIndex, frame.path[pathIndex]);
|
||||
}
|
||||
putchar ('\n');
|
||||
}
|
||||
|
||||
void sendFrame (FrameStruct frame) {
|
||||
uint8_t txBuf[256];
|
||||
size_t offset = 0;
|
||||
|
||||
txBuf[offset++] = frame.header;
|
||||
|
||||
if ((frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT ||
|
||||
(frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
memcpy (txBuf + offset, frame.transportCodes, 4);
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
if (frame.pathLen > 64) {
|
||||
frame.pathLen = 64;
|
||||
}
|
||||
|
||||
txBuf[offset++] = frame.pathLen;
|
||||
|
||||
memcpy (txBuf + offset, frame.path, frame.pathLen);
|
||||
offset += frame.pathLen;
|
||||
|
||||
uint16_t maxPayloadLen = 256 - offset;
|
||||
|
||||
uint16_t payloadLen = frame.payloadLen > maxPayloadLen ? maxPayloadLen : frame.payloadLen;
|
||||
|
||||
memcpy (txBuf + offset, frame.payload, payloadLen);
|
||||
offset += payloadLen;
|
||||
|
||||
hexdump ("TxDump", txBuf, offset);
|
||||
LoRaSend (txBuf, offset, SX126x_TXMODE_SYNC);
|
||||
}
|
||||
|
||||
void sendGroupMessage (GroupTextMessage msg) {
|
||||
|
||||
msg.channelHash = aesKeys[msg.keyIndex][0];
|
||||
|
||||
msg.flags = 0;
|
||||
msg.timestamp = RTC_GetCounter();
|
||||
|
||||
FrameStruct frame;
|
||||
frame.header = ROUTE_TYPE_FLOOD | PAYLOAD_TYPE_GRP_TXT | PAYLOAD_VERSION_0;
|
||||
frame.pathLen = 0;
|
||||
size_t offset = 0;
|
||||
memset (frame.payload, 0, sizeof (frame.payload));
|
||||
frame.payload[offset++] = msg.channelHash;
|
||||
|
||||
uint8_t cipherBuf[176];
|
||||
size_t offset2 = 0;
|
||||
memcpy (cipherBuf, (const void *)&(msg.timestamp), 4);
|
||||
offset2 += 4;
|
||||
cipherBuf[offset2++] = msg.flags;
|
||||
size_t textSize = offset2 + strlen ((const char *)msg.text);
|
||||
if (textSize > 175) {
|
||||
textSize = 175;
|
||||
}
|
||||
memcpy (cipherBuf + offset2, msg.text, textSize);
|
||||
offset2 += textSize;
|
||||
|
||||
offset2 = (((offset2 / 16) + 1) * 16);
|
||||
|
||||
size_t olen = 0;
|
||||
hexdump ("TxDumpDec", cipherBuf, offset2);
|
||||
encrypt_then_mac (&(aesKeys[msg.keyIndex][1]), cipherBuf, offset2, &(frame.payload[offset]), &olen);
|
||||
|
||||
frame.payloadLen = olen + 1;
|
||||
|
||||
sendFrame (frame);
|
||||
return;
|
||||
}
|
||||
|
||||
void makeSendGroupMessage (char *txt, uint8_t keyIndex) {
|
||||
GroupTextMessage msg;
|
||||
strcpy ((char *)msg.text, txt);
|
||||
msg.keyIndex = keyIndex;
|
||||
sendGroupMessage (msg);
|
||||
return;
|
||||
}
|
||||
|
||||
void sendAdvert() {
|
||||
AdvertisementPayload ad;
|
||||
memcpy (ad.pubKey, persistent.pubkey, sizeof (ad.pubKey));
|
||||
ad.dataFlags = ADVERTISEMENT_FLAG_IS_CHAT_NODE | ADVERTISEMENT_FLAG_HAS_NAME;
|
||||
strcpy (ad.nodeName, persistent.nodeName);
|
||||
ad.timestamp = RTC_GetCounter();
|
||||
|
||||
// 1. Build app_data exactly like AdvertDataBuilder::encodeTo
|
||||
uint8_t app_data[40];
|
||||
size_t app_len = 0;
|
||||
|
||||
app_data[app_len++] = ad.dataFlags;
|
||||
|
||||
if (ad.dataFlags & ADVERTISEMENT_FLAG_HAS_LOCATION) {
|
||||
memcpy (app_data + app_len, &ad.latitude, sizeof (ad.latitude));
|
||||
app_len += sizeof (ad.latitude);
|
||||
memcpy (app_data + app_len, &ad.longitude, sizeof (ad.longitude));
|
||||
app_len += sizeof (ad.longitude);
|
||||
}
|
||||
|
||||
if (ad.dataFlags & ADVERTISEMENT_FLAG_RFU1) {
|
||||
memcpy (app_data + app_len, &ad.rfu1, sizeof (ad.rfu1));
|
||||
app_len += sizeof (ad.rfu1);
|
||||
}
|
||||
|
||||
if (ad.dataFlags & ADVERTISEMENT_FLAG_RFU2) {
|
||||
memcpy (app_data + app_len, &ad.rfu2, sizeof (ad.rfu2));
|
||||
app_len += sizeof (ad.rfu2);
|
||||
}
|
||||
|
||||
if (ad.dataFlags & ADVERTISEMENT_FLAG_HAS_NAME) {
|
||||
size_t nodenameLen = strlen (ad.nodeName);
|
||||
memcpy (app_data + app_len, ad.nodeName, nodenameLen);
|
||||
app_len += nodenameLen;
|
||||
}
|
||||
|
||||
// 2. Reserve frame and build payload header
|
||||
FrameStruct frame;
|
||||
frame.header = ROUTE_TYPE_FLOOD | PAYLOAD_TYPE_ADVERT | PAYLOAD_VERSION_0;
|
||||
|
||||
size_t offset = 0;
|
||||
memcpy (frame.payload + offset, ad.pubKey, sizeof (ad.pubKey));
|
||||
offset += sizeof (ad.pubKey);
|
||||
|
||||
memcpy (frame.payload + offset, &ad.timestamp, sizeof (ad.timestamp));
|
||||
offset += sizeof (ad.timestamp);
|
||||
|
||||
// reserve signature space
|
||||
uint8_t *signature_pos = frame.payload + offset;
|
||||
offset += 64;
|
||||
|
||||
// append app_data after signature
|
||||
memcpy (frame.payload + offset, app_data, app_len);
|
||||
offset += app_len;
|
||||
|
||||
// 3. Sign pubKey + timestamp + app_data
|
||||
uint8_t message[76];
|
||||
size_t msg_len = 0;
|
||||
memcpy (message + msg_len, ad.pubKey, sizeof (ad.pubKey));
|
||||
msg_len += sizeof (ad.pubKey);
|
||||
memcpy (message + msg_len, &ad.timestamp, sizeof (ad.timestamp));
|
||||
msg_len += sizeof (ad.timestamp);
|
||||
memcpy (message + msg_len, app_data, app_len);
|
||||
msg_len += app_len;
|
||||
|
||||
crypto_ed25519_meshcore_sign (signature_pos, persistent.privkey, persistent.pubkey, message, msg_len);
|
||||
|
||||
hexdump ("Complete advert", frame.payload, offset);
|
||||
|
||||
hexdump ("Public key", ad.pubKey, 32);
|
||||
|
||||
hexdump ("Signature", signature_pos, 64);
|
||||
|
||||
hexdump ("Timestamp", &ad.timestamp, 4);
|
||||
|
||||
hexdump ("NodeName", ad.nodeName, 20);
|
||||
|
||||
hexdump ("Appdata", app_data, app_len);
|
||||
|
||||
// 5. Set payload length and send
|
||||
frame.payloadLen = offset;
|
||||
frame.pathLen = 0;
|
||||
sendFrame (frame);
|
||||
retransmitFrame (frame);
|
||||
}
|
||||
|
||||
@@ -7,55 +7,10 @@
|
||||
#include "lib/cifra/aes.h"
|
||||
#include "lib/cifra/sha2.h"
|
||||
#include "lib/cifra/hmac.h"
|
||||
#include "lib/base64.h"
|
||||
#include "util/log.h"
|
||||
#include <ctype.h>
|
||||
#include "stdio.h"
|
||||
|
||||
// requires at least a 256 byte data
|
||||
FrameStruct decodeFrame (unsigned char *data, unsigned char dataLen);
|
||||
|
||||
#define KEY_SIZE 16 // 128-bit AES
|
||||
#define HMAC_SIZE 2 // SHA256 output size
|
||||
|
||||
int aes_encrypt_ecb (const uint8_t *key, const uint8_t *input, size_t ilen,
|
||||
uint8_t *output);
|
||||
|
||||
// AES-ECB decrypt (same as Arduino's aes.decryptBlock)
|
||||
int aes_decrypt_ecb (const uint8_t *key, const uint8_t *input, size_t ilen,
|
||||
uint8_t *output);
|
||||
|
||||
// HMAC-SHA256
|
||||
int hmac_sha256 (const uint8_t *key, size_t keylen, const uint8_t *input,
|
||||
size_t ilen, uint8_t *output);
|
||||
|
||||
// Verify MAC + Decrypt
|
||||
int mac_then_decrypt (const uint8_t *aes_key, const uint8_t *input, size_t ilen,
|
||||
uint8_t *plaintext);
|
||||
|
||||
void hexdump (const char *label, const uint8_t *data, size_t len);
|
||||
|
||||
#define AESKeyCount 8
|
||||
|
||||
// EncryptedPayloadStruct decodeEncryptedPayload(FrameStruct frame);
|
||||
|
||||
GroupTextMessage decodeGroupMessage (FrameStruct frame);
|
||||
|
||||
void printGroupMessage (GroupTextMessage msg);
|
||||
|
||||
AdvertisementPayload decodeAdvertisement (FrameStruct frame);
|
||||
|
||||
void printAdvertisement (AdvertisementPayload advert);
|
||||
|
||||
void printFrameHeader (FrameStruct frame);
|
||||
|
||||
#define AESKeyCount 8
|
||||
|
||||
extern const uint8_t aesKeys[AESKeyCount][17];
|
||||
|
||||
void sendFrame (FrameStruct frame);
|
||||
void sendGroupMessage (GroupTextMessage msg);
|
||||
void makeSendGroupMessage (char *txt, uint8_t keyIndex);
|
||||
void sendAdvert();
|
||||
void processFrame (FrameStruct frame);
|
||||
|
||||
#endif
|
||||
214
User/meshcore/meshframing.c
Normal file
214
User/meshcore/meshframing.c
Normal file
@@ -0,0 +1,214 @@
|
||||
#include "meshframing.h"
|
||||
#include "lib/config.h"
|
||||
#include "string.h"
|
||||
#include "stdio.h"
|
||||
#include "sx1262.h"
|
||||
#include "util/hexdump.h"
|
||||
|
||||
FrameStruct decodeFrame (unsigned char *data, unsigned char dataLen) {
|
||||
hexdump ("RxDump", data, dataLen);
|
||||
FrameStruct frame;
|
||||
memset (&frame, 0, sizeof (frame));
|
||||
unsigned char index = 0;
|
||||
frame.header = data[index++];
|
||||
if ((frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT ||
|
||||
(frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
memcpy (frame.transportCodes, data + index, 4);
|
||||
index += 4;
|
||||
}
|
||||
frame.path.pathLen = data[index++];
|
||||
|
||||
memcpy (frame.path.path, data + index, frame.path.pathLen);
|
||||
index += frame.path.pathLen;
|
||||
frame.payloadLen = dataLen - index;
|
||||
memcpy (frame.payload, data + index, frame.payloadLen);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void printFrameHeader (FrameStruct frame) {
|
||||
switch (frame.header & ROUTE_TYPE_MASK) {
|
||||
case ROUTE_TYPE_TRANSPORT_FLOOD:
|
||||
printf ("transport flood");
|
||||
break;
|
||||
|
||||
case ROUTE_TYPE_FLOOD:
|
||||
printf ("flood");
|
||||
break;
|
||||
|
||||
case ROUTE_TYPE_DIRECT:
|
||||
printf ("direct");
|
||||
break;
|
||||
|
||||
case ROUTE_TYPE_TRANSPORT_DIRECT:
|
||||
printf ("transport direct");
|
||||
break;
|
||||
}
|
||||
|
||||
printf (", payload type is ");
|
||||
|
||||
switch (frame.header & PAYLOAD_TYPE_MASK) {
|
||||
case PAYLOAD_TYPE_REQ:
|
||||
printf ("request");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_RESPONSE:
|
||||
printf ("response");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_TXT_MSG:
|
||||
printf ("text message");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_ACK:
|
||||
printf ("acknowledgement");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_ADVERT:
|
||||
printf ("advert");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_GRP_TXT:
|
||||
printf ("group text");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_GRP_DATA:
|
||||
printf ("group data");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_ANON_REQ:
|
||||
printf ("anon request");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_PATH:
|
||||
printf ("path");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_TRACE:
|
||||
printf ("trace");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_MULTIPART:
|
||||
printf ("multipart");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_CONTROL:
|
||||
printf ("control");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_RAW_CUSTOM:
|
||||
printf ("raw");
|
||||
break;
|
||||
}
|
||||
char version[2];
|
||||
version[0] = (frame.header >> 6) + '0';
|
||||
version[1] = 0;
|
||||
|
||||
printf (", payload version is %s ", version);
|
||||
|
||||
if ((frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT ||
|
||||
(frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
printf ("Transport codes: %d %d\n", *((uint16_t *)frame.transportCodes),
|
||||
*((uint16_t *)&(frame.transportCodes[2])));
|
||||
}
|
||||
printf ("Path is %d nodes long", frame.path.pathLen);
|
||||
|
||||
for (uint8_t pathIndex = 0; pathIndex < frame.path.pathLen; pathIndex++) {
|
||||
printf ("node %d - %02X, ", pathIndex, frame.path.path[pathIndex]);
|
||||
}
|
||||
putchar ('\n');
|
||||
}
|
||||
|
||||
void sendFrame (FrameStruct frame) {
|
||||
uint8_t txBuf[256];
|
||||
size_t offset = 0;
|
||||
|
||||
txBuf[offset++] = frame.header;
|
||||
|
||||
if ((frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT ||
|
||||
(frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
memcpy (txBuf + offset, frame.transportCodes, 4);
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
if (frame.path.pathLen > 64) {
|
||||
frame.path.pathLen = 64;
|
||||
}
|
||||
|
||||
txBuf[offset++] = frame.path.pathLen;
|
||||
|
||||
memcpy (txBuf + offset, frame.path.path, frame.path.pathLen);
|
||||
offset += frame.path.pathLen;
|
||||
|
||||
uint16_t maxPayloadLen = 256 - offset;
|
||||
|
||||
uint16_t payloadLen = frame.payloadLen > maxPayloadLen ? maxPayloadLen : frame.payloadLen;
|
||||
|
||||
memcpy (txBuf + offset, frame.payload, payloadLen);
|
||||
offset += payloadLen;
|
||||
|
||||
hexdump ("TxDump", txBuf, offset);
|
||||
LoRaSend (txBuf, offset, SX126x_TXMODE_SYNC);
|
||||
}
|
||||
|
||||
void retransmitFrame (FrameStruct frame) {
|
||||
if (frame.header & ROUTE_TYPE_FLOOD || frame.header & ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
if (frame.header != DONT_RETRANSMIT_HEADER && frame.path.pathLen + 1 < MAX_FLOOD_TTL) {
|
||||
frame.path.path[frame.path.pathLen++] = persistent.pubkey[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (frame.header & ROUTE_TYPE_DIRECT || frame.header & ROUTE_TYPE_TRANSPORT_DIRECT) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Verify MAC + Decrypt
|
||||
|
||||
int encrypt_then_mac (const uint8_t *aes_key, const uint8_t keySize, const uint8_t *plaintext, size_t plen, uint8_t *output, size_t *olen) {
|
||||
if (plen == 0)
|
||||
return -1;
|
||||
|
||||
size_t padded_len = ((plen + 15) / 16) * 16;
|
||||
|
||||
// prepare padded buffer
|
||||
uint8_t padded[padded_len];
|
||||
memset(padded, 0, padded_len); // zero padding
|
||||
memcpy(padded, plaintext, plen); // copy plaintext
|
||||
|
||||
// ciphertext will go right after HMAC
|
||||
uint8_t *ciphertext = output + HMAC_SIZE;
|
||||
|
||||
// encrypt plaintext
|
||||
aes_encrypt_ecb (aes_key, 16, padded, padded_len, ciphertext);
|
||||
|
||||
// compute HMAC over ciphertext
|
||||
uint8_t mac[32]; // full SHA-256
|
||||
hmac_sha256 (aes_key, keySize, ciphertext, padded_len, mac);
|
||||
|
||||
// copy only HMAC_SIZE bytes of MAC
|
||||
memcpy (output, mac, HMAC_SIZE);
|
||||
|
||||
// return total length = HMAC + ciphertext
|
||||
*olen = HMAC_SIZE + padded_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_then_decrypt (const uint8_t *aes_key, const uint8_t keySize, const uint8_t *input, size_t ilen, uint8_t *plaintext) {
|
||||
if (ilen <= HMAC_SIZE)
|
||||
return -1;
|
||||
|
||||
const uint8_t *mac = input;
|
||||
const uint8_t *ciphertext = input + HMAC_SIZE;
|
||||
size_t clen = ilen - HMAC_SIZE;
|
||||
|
||||
if (clen % 16 != 0) return -2; // must be multiple of block size
|
||||
|
||||
uint8_t calc_mac[32]; // full SHA-256
|
||||
hmac_sha256 (aes_key, keySize, ciphertext, clen, calc_mac);
|
||||
|
||||
if (memcmp (mac, calc_mac, HMAC_SIZE) != 0)
|
||||
return -2;
|
||||
|
||||
return aes_decrypt_ecb (aes_key, 16, ciphertext, clen, plaintext);
|
||||
}
|
||||
29
User/meshcore/meshframing.h
Normal file
29
User/meshcore/meshframing.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef MESHCORE_FRAME_HEADER
|
||||
#define MESHCORE_FRAME_HEADER
|
||||
|
||||
#include "stddef.h"
|
||||
#include "stdint.h"
|
||||
#include "lib/cifra/aes.h"
|
||||
#include "lib/cifra/hmac.h"
|
||||
#include "packetstructs.h"
|
||||
|
||||
|
||||
#define KEY_SIZE 16 // 128-bit AES
|
||||
#define HMAC_SIZE 2 // meshcore size
|
||||
#define MAX_FLOOD_TTL 64
|
||||
|
||||
FrameStruct decodeFrame (unsigned char *data, unsigned char dataLen);
|
||||
|
||||
void printFrameHeader (FrameStruct frame) ;
|
||||
|
||||
void sendFrame (FrameStruct frame);
|
||||
|
||||
void retransmitFrame (FrameStruct frame) ;
|
||||
|
||||
// Verify MAC + Decrypt
|
||||
|
||||
int encrypt_then_mac (const uint8_t *aes_key, const uint8_t keySize, const uint8_t *plaintext, size_t plen, uint8_t *output, size_t *olen);
|
||||
|
||||
int mac_then_decrypt (const uint8_t *aes_key, const uint8_t keySize, const uint8_t *input, size_t ilen, uint8_t *plaintext);
|
||||
|
||||
#endif
|
||||
42
User/meshcore/packets/ack.c
Normal file
42
User/meshcore/packets/ack.c
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "ack.h"
|
||||
#include "lib/cifra/sha2.h"
|
||||
#include "lib/config.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "Ack"
|
||||
|
||||
void sendDiscreteAck (uint8_t *data, const uint8_t len, uint8_t *senderPubKey) {
|
||||
FrameStruct frame;
|
||||
memset (&frame, 0, sizeof (frame));
|
||||
|
||||
// 1. Header
|
||||
frame.header =
|
||||
ROUTE_TYPE_FLOOD | // currently flood
|
||||
PAYLOAD_TYPE_ACK |
|
||||
PAYLOAD_VERSION_0;
|
||||
|
||||
// Buffer for the digest
|
||||
uint8_t hash[CF_SHA256_HASHSZ];
|
||||
|
||||
// Context
|
||||
cf_sha256_context ctx;
|
||||
|
||||
// 1. Initialize
|
||||
cf_sha256_init (&ctx);
|
||||
|
||||
// 2. Feed in your data
|
||||
cf_sha256_update (&ctx, data, len);
|
||||
|
||||
cf_sha256_update (&ctx, senderPubKey, sizeof (persistent.pubkey));
|
||||
|
||||
// 3. Compute digest
|
||||
cf_sha256_digest (&ctx, hash);
|
||||
|
||||
memcpy (frame.payload, hash, 4);
|
||||
// 5. Finalize
|
||||
frame.payloadLen = 4;
|
||||
|
||||
|
||||
sendFrame (frame);
|
||||
}
|
||||
8
User/meshcore/packets/ack.h
Normal file
8
User/meshcore/packets/ack.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef ACK_HEADER
|
||||
#define ACK_HEADER
|
||||
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
void sendDiscreteAck (uint8_t *data, const uint8_t len, uint8_t *senderPubKey);
|
||||
|
||||
#endif
|
||||
192
User/meshcore/packets/advert.c
Normal file
192
User/meshcore/packets/advert.c
Normal file
@@ -0,0 +1,192 @@
|
||||
#include "ch32v30x_rtc.h"
|
||||
#include "lib/config.h"
|
||||
#include "lib/ed25519/ed_25519.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include <string.h>
|
||||
#include "advert.h"
|
||||
#include "util/hexdump.h"
|
||||
#include "lib/base64.h"
|
||||
|
||||
#define TAG "Advert"
|
||||
|
||||
void sendAdvert() {
|
||||
AdvertisementPayload ad;
|
||||
memcpy (ad.pubKey, persistent.pubkey, sizeof (ad.pubKey));
|
||||
ad.dataFlags = ADVERTISEMENT_FLAG_HAS_NAME;
|
||||
if (persistent.nodeType == NODE_TYPE_CHAT_NODE) {
|
||||
ad.dataFlags |= ADVERTISEMENT_FLAG_IS_CHAT_NODE;
|
||||
} else if (persistent.nodeType == NODE_TYPE_REPEATER) {
|
||||
ad.dataFlags |= ADVERTISEMENT_FLAG_IS_REAPEATER;
|
||||
} else if (persistent.nodeType == NODE_TYPE_ROOM_SERVER) {
|
||||
ad.dataFlags |= ADVERTISEMENT_FLAG_IS_ROOM_SERVER;
|
||||
} else if (persistent.nodeType == NODE_TYPE_SENSOR) {
|
||||
ad.dataFlags |= ADVERTISEMENT_FLAG_IS_SENSOR;
|
||||
}
|
||||
strcpy (ad.nodeName, persistent.nodeName);
|
||||
ad.timestamp = RTC_GetCounter();
|
||||
|
||||
uint8_t app_data[40];
|
||||
size_t app_len = 0;
|
||||
|
||||
app_data[app_len++] = ad.dataFlags;
|
||||
|
||||
if (ad.dataFlags & ADVERTISEMENT_FLAG_HAS_LOCATION) {
|
||||
memcpy (app_data + app_len, &ad.latitude, sizeof (ad.latitude));
|
||||
app_len += sizeof (ad.latitude);
|
||||
memcpy (app_data + app_len, &ad.longitude, sizeof (ad.longitude));
|
||||
app_len += sizeof (ad.longitude);
|
||||
}
|
||||
|
||||
if (ad.dataFlags & ADVERTISEMENT_FLAG_RFU1) {
|
||||
memcpy (app_data + app_len, &ad.rfu1, sizeof (ad.rfu1));
|
||||
app_len += sizeof (ad.rfu1);
|
||||
}
|
||||
|
||||
if (ad.dataFlags & ADVERTISEMENT_FLAG_RFU2) {
|
||||
memcpy (app_data + app_len, &ad.rfu2, sizeof (ad.rfu2));
|
||||
app_len += sizeof (ad.rfu2);
|
||||
}
|
||||
|
||||
if (ad.dataFlags & ADVERTISEMENT_FLAG_HAS_NAME) {
|
||||
size_t nodenameLen = strlen (ad.nodeName);
|
||||
memcpy (app_data + app_len, ad.nodeName, nodenameLen);
|
||||
app_len += nodenameLen;
|
||||
}
|
||||
|
||||
// 2. Reserve frame and build payload header
|
||||
FrameStruct frame;
|
||||
frame.header = ROUTE_TYPE_FLOOD | PAYLOAD_TYPE_ADVERT | PAYLOAD_VERSION_0;
|
||||
|
||||
size_t offset = 0;
|
||||
memcpy (frame.payload + offset, ad.pubKey, sizeof (ad.pubKey));
|
||||
offset += sizeof (ad.pubKey);
|
||||
|
||||
memcpy (frame.payload + offset, &ad.timestamp, sizeof (ad.timestamp));
|
||||
offset += sizeof (ad.timestamp);
|
||||
|
||||
// reserve signature space
|
||||
uint8_t *signature_pos = frame.payload + offset;
|
||||
offset += 64;
|
||||
|
||||
// append app_data after signature
|
||||
memcpy (frame.payload + offset, app_data, app_len);
|
||||
offset += app_len;
|
||||
|
||||
// 3. Sign pubKey + timestamp + app_data
|
||||
uint8_t message[76];
|
||||
size_t msg_len = 0;
|
||||
memcpy (message + msg_len, ad.pubKey, sizeof (ad.pubKey));
|
||||
msg_len += sizeof (ad.pubKey);
|
||||
memcpy (message + msg_len, &ad.timestamp, sizeof (ad.timestamp));
|
||||
msg_len += sizeof (ad.timestamp);
|
||||
memcpy (message + msg_len, app_data, app_len);
|
||||
msg_len += app_len;
|
||||
|
||||
ed25519_sign (signature_pos, message, msg_len, persistent.pubkey, persistent.privkey);
|
||||
|
||||
hexdump ("Complete advert", frame.payload, offset);
|
||||
|
||||
hexdump ("Public key", ad.pubKey, 32);
|
||||
|
||||
hexdump ("Signature", signature_pos, 64);
|
||||
|
||||
printf ("Timestamp is %d\n", ad.timestamp);
|
||||
|
||||
printf ("NodeName %s\n", ad.nodeName);
|
||||
|
||||
hexdump ("Appdata", app_data, app_len);
|
||||
|
||||
// 5. Set payload length and send
|
||||
frame.payloadLen = offset;
|
||||
frame.path.pathLen = 0;
|
||||
sendFrame (frame);
|
||||
}
|
||||
|
||||
AdvertisementPayload decodeAdvertisement (FrameStruct frame) {
|
||||
AdvertisementPayload advert;
|
||||
memset (&advert, 0, sizeof (advert));
|
||||
|
||||
if ((frame.header & PAYLOAD_TYPE_MASK) != PAYLOAD_TYPE_ADVERT) {
|
||||
return advert;
|
||||
}
|
||||
|
||||
unsigned char index = 0;
|
||||
|
||||
memcpy (advert.pubKey, frame.payload + index, 32);
|
||||
index += 32;
|
||||
|
||||
memcpy (&advert.timestamp, frame.payload + index, 4);
|
||||
index += 4;
|
||||
|
||||
memcpy (advert.signature, frame.payload + index, 64);
|
||||
index += 64;
|
||||
|
||||
advert.dataFlags = frame.payload[index++];
|
||||
|
||||
if (advert.dataFlags & ADVERTISEMENT_FLAG_HAS_LOCATION) {
|
||||
memcpy (&advert.latitude, frame.payload + index, 4);
|
||||
index += 4;
|
||||
memcpy (&advert.longitude, frame.payload + index, 4);
|
||||
index += 4;
|
||||
}
|
||||
|
||||
if (advert.dataFlags & ADVERTISEMENT_FLAG_RFU1) {
|
||||
memcpy (&advert.rfu1, frame.payload + index, 2);
|
||||
index += 2;
|
||||
}
|
||||
if (advert.dataFlags & ADVERTISEMENT_FLAG_RFU2) {
|
||||
memcpy (&advert.rfu2, frame.payload + index, 2);
|
||||
index += 2;
|
||||
}
|
||||
unsigned char nameLen = frame.payloadLen - index;
|
||||
if (nameLen > 32) {
|
||||
nameLen = 32;
|
||||
}
|
||||
memcpy (advert.nodeName, frame.payload + index, nameLen);
|
||||
advert.nodeName[frame.payloadLen - index] = 0;
|
||||
|
||||
|
||||
NodeEntry *node = getNode (advert.pubKey[0]);
|
||||
|
||||
if (node == NULL) {
|
||||
node = getNextNode();
|
||||
memset (node, 0, sizeof (NodeEntry));
|
||||
}
|
||||
|
||||
memcpy (node->name, advert.nodeName, sizeof (node->name));
|
||||
memcpy (node->pubKey, advert.pubKey, sizeof (node->pubKey));
|
||||
ed25519_key_exchange ((unsigned char *)node->secret, advert.pubKey, persistent.privkey);
|
||||
node->gps_latitude = advert.latitude;
|
||||
node->gps_longitude = advert.longitude;
|
||||
// ADD PATH
|
||||
node->type = advert.dataFlags & 0x0F;
|
||||
node->last_seen_lt = RTC_GetCounter();
|
||||
node->last_seen_rt = advert.timestamp;
|
||||
|
||||
|
||||
return advert;
|
||||
}
|
||||
|
||||
void printAdvertisement (AdvertisementPayload advert) {
|
||||
unsigned char keyBuf[50];
|
||||
unsigned char sigBuf[90];
|
||||
memset (keyBuf, 0, sizeof (keyBuf));
|
||||
memset (sigBuf, 0, sizeof (sigBuf));
|
||||
base64_encode (advert.pubKey, 32, keyBuf);
|
||||
base64_encode (advert.signature, 64, sigBuf);
|
||||
|
||||
printf ("%s on %ld with type %s on %s location %ld %ld, public key %s and "
|
||||
"signature %s\n",
|
||||
advert.dataFlags & ADVERTISEMENT_FLAG_HAS_NAME ? advert.nodeName
|
||||
: "nameless node",
|
||||
advert.timestamp,
|
||||
(advert.dataFlags & 0x07) == 0x04
|
||||
? "sensor"
|
||||
: ((advert.dataFlags & 0x07) == 0x03
|
||||
? "room server"
|
||||
: ((advert.dataFlags & 0x07) == 0x02 ? "repeater"
|
||||
: "chat node")),
|
||||
advert.dataFlags & 0x80 ? "known" : "unknown", advert.latitude,
|
||||
advert.longitude, keyBuf, sigBuf);
|
||||
}
|
||||
12
User/meshcore/packets/advert.h
Normal file
12
User/meshcore/packets/advert.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef ADVERT_HEADER
|
||||
#define ADVERT_HEADER
|
||||
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
void sendAdvert();
|
||||
|
||||
AdvertisementPayload decodeAdvertisement (FrameStruct frame);
|
||||
|
||||
void printAdvertisement (AdvertisementPayload advert);
|
||||
|
||||
#endif
|
||||
133
User/meshcore/packets/anonymous.c
Normal file
133
User/meshcore/packets/anonymous.c
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "ch32v30x_rtc.h"
|
||||
#include "lib/config.h"
|
||||
#include "lib/ed25519/ed_25519.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include "meshcore/packets/encrypted.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "anonymous.h"
|
||||
#include "util/hexdump.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define TAG "Anonymous"
|
||||
|
||||
void sendAnonymousRequest (NodeEntry *targetNode, const uint8_t *password, uint32_t sync) {
|
||||
uint8_t passwordLen = strlen ((const char *)password);
|
||||
FrameStruct frame;
|
||||
uint8_t offset = 0;
|
||||
|
||||
// 1. Frame header
|
||||
frame.header = ((targetNode->path.pathLen > 0) ? ROUTE_TYPE_DIRECT : ROUTE_TYPE_FLOOD) | PAYLOAD_TYPE_ANON_REQ | PAYLOAD_VERSION_0;
|
||||
|
||||
// 2. Payload header (unencrypted)
|
||||
frame.payload[offset++] = targetNode->pubKey[0];
|
||||
|
||||
memcpy (frame.payload + offset, persistent.pubkey, 32);
|
||||
offset += 32;
|
||||
|
||||
// 3. Build plaintext payload
|
||||
uint8_t plaintext[32];
|
||||
uint8_t p = 0;
|
||||
|
||||
uint32_t last_seen_rt = RTC_GetCounter();
|
||||
plaintext[p++] = (last_seen_rt >> 0) & 0xFF;
|
||||
plaintext[p++] = (last_seen_rt >> 8) & 0xFF;
|
||||
plaintext[p++] = (last_seen_rt >> 16) & 0xFF;
|
||||
plaintext[p++] = (last_seen_rt >> 24) & 0xFF;
|
||||
|
||||
if (targetNode->type == NODE_TYPE_ROOM_SERVER) {
|
||||
plaintext[p++] = (sync >> 0) & 0xFF;
|
||||
plaintext[p++] = (sync >> 8) & 0xFF;
|
||||
plaintext[p++] = (sync >> 16) & 0xFF;
|
||||
plaintext[p++] = (sync >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
if (passwordLen > 16) {
|
||||
passwordLen = 16;
|
||||
}
|
||||
memcpy (plaintext + p, password, passwordLen);
|
||||
p += passwordLen;
|
||||
|
||||
size_t outputLen;
|
||||
// 4. Encrypt + MAC
|
||||
encrypt_then_mac (
|
||||
targetNode->secret,
|
||||
32,
|
||||
plaintext,
|
||||
p,
|
||||
frame.payload + offset,
|
||||
&outputLen);
|
||||
|
||||
offset += outputLen;
|
||||
|
||||
// 5. Finalize and send
|
||||
frame.payloadLen = offset;
|
||||
memcpy(&(frame.path), &(targetNode->path), sizeof(frame.path));
|
||||
|
||||
hexdump ("Anon payload", frame.payload, frame.payloadLen);
|
||||
|
||||
sendFrame (frame);
|
||||
}
|
||||
|
||||
AnonymousRequestPayload decodeAnonReq (FrameStruct frame) {
|
||||
uint8_t index = 0;
|
||||
AnonymousRequestPayload anonReq;
|
||||
anonReq.destinationHash = frame.payload[index++];
|
||||
memcpy (anonReq.pubKey, &(frame.payload[index]), sizeof (anonReq.pubKey));
|
||||
index += sizeof (anonReq.pubKey);
|
||||
anonReq.cipherMAC = frame.payload[index];
|
||||
anonReq.cipherMAC |= frame.payload[index + 1] << 8;
|
||||
NodeEntry *foundNode = getNode (anonReq.pubKey[0]);
|
||||
if (foundNode == NULL) {
|
||||
foundNode = getNextNode();
|
||||
|
||||
strcpy (foundNode->name, "Anonymous node");
|
||||
memcpy (foundNode->pubKey, anonReq.pubKey, sizeof (foundNode->pubKey));
|
||||
ed25519_key_exchange ((unsigned char *)foundNode->secret, anonReq.pubKey, persistent.privkey);
|
||||
foundNode->gps_latitude = 0;
|
||||
foundNode->gps_longitude = 0;
|
||||
// ADD PATH
|
||||
foundNode->type = 0;
|
||||
foundNode->last_seen_lt = RTC_GetCounter();
|
||||
}
|
||||
mac_then_decrypt (foundNode->secret, 32, &(frame.payload[index]), frame.payloadLen - index, anonReq.payload);
|
||||
anonReq.payloadLen = frame.payloadLen - index - 2;
|
||||
uint8_t index2 = 0;
|
||||
foundNode->last_seen_rt = anonReq.payload[index2++];
|
||||
foundNode->last_seen_rt |= anonReq.payload[index2++] << 8;
|
||||
foundNode->last_seen_rt |= anonReq.payload[index2++] << 16;
|
||||
foundNode->last_seen_rt |= anonReq.payload[index2++] << 24;
|
||||
|
||||
if (persistent.nodeType == NODE_TYPE_ROOM_SERVER) {
|
||||
foundNode->sync_timestamp = anonReq.payload[index2++];
|
||||
foundNode->sync_timestamp |= anonReq.payload[index2++] << 8;
|
||||
foundNode->sync_timestamp |= anonReq.payload[index2++] << 16;
|
||||
foundNode->sync_timestamp |= anonReq.payload[index2++] << 24;
|
||||
}
|
||||
|
||||
uint8_t passwordLen = anonReq.payloadLen - index2;
|
||||
if (passwordLen > 16) {
|
||||
passwordLen = 16;
|
||||
}
|
||||
uint8_t passwordBuf[16];
|
||||
memcpy (passwordBuf, &(anonReq.payload[index2]), passwordLen);
|
||||
if (memcmp (passwordBuf, persistent.password, sizeof (persistent.password)) == 0) {
|
||||
foundNode->authenticated = 1;
|
||||
Response resp;
|
||||
resp.tag = RTC_GetCounter();
|
||||
uint8_t index3 = 0;
|
||||
uint32_t randOut = rand();
|
||||
resp.data[index3++] = RESP_SERVER_LOGIN_OK;
|
||||
resp.data[index3++] = 0;//legacy
|
||||
resp.data[index3++] = 1;//isadmin
|
||||
resp.data[index3++] = PERM_ACL_ADMIN;//permissions
|
||||
resp.data[index3++] = randOut & 0xFF;//rng
|
||||
resp.data[index3++] = (randOut >> 8) & 0xFF;
|
||||
resp.data[index3++] = (randOut >> 16) & 0xFF;
|
||||
resp.data[index3++] = (randOut >> 24) & 0xFF;
|
||||
resp.data[index3++] = FIRMWARE_VER_LEVEL;
|
||||
resp.dataLen = index3;
|
||||
sendEncryptedResponse(foundNode, &resp);
|
||||
}
|
||||
|
||||
return anonReq;
|
||||
}
|
||||
11
User/meshcore/packets/anonymous.h
Normal file
11
User/meshcore/packets/anonymous.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef ANONYMOUS_HEADER
|
||||
#define ANONYMOUS_HEADER
|
||||
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "lib/config.h"
|
||||
|
||||
AnonymousRequestPayload decodeAnonReq (FrameStruct frame);
|
||||
|
||||
void sendAnonymousRequest (NodeEntry *targetNode, const uint8_t *password, uint32_t sync);
|
||||
|
||||
#endif
|
||||
76
User/meshcore/packets/control.c
Normal file
76
User/meshcore/packets/control.c
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "meshcore/meshframing.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "control.h"
|
||||
#include "string.h"
|
||||
|
||||
#define TAG "Control"
|
||||
|
||||
void sendDiscoverRequest(const DiscoverRequestPayload *discReq) {
|
||||
|
||||
FrameStruct frame;
|
||||
uint8_t offset = 0;
|
||||
|
||||
// Build payload
|
||||
frame.payload[offset++] = (discReq->prefixOnly & 0x01) | CONTROL_DATA_FLAG_TYPE_NODE_DISCOVER_REQ;
|
||||
|
||||
frame.payload[offset++] = discReq->typeFilter;
|
||||
|
||||
frame.payload[offset++] = (discReq->tag >> 0) & 0xFF;
|
||||
frame.payload[offset++] = (discReq->tag >> 8) & 0xFF;
|
||||
frame.payload[offset++] = (discReq->tag >> 16) & 0xFF;
|
||||
frame.payload[offset++] = (discReq->tag >> 24) & 0xFF;
|
||||
|
||||
// optional `since`
|
||||
if (discReq->since != 0) { // or another condition if you want to always include
|
||||
frame.payload[offset++] = (discReq->since >> 0) & 0xFF;
|
||||
frame.payload[offset++] = (discReq->since >> 8) & 0xFF;
|
||||
frame.payload[offset++] = (discReq->since >> 16) & 0xFF;
|
||||
frame.payload[offset++] = (discReq->since >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
frame.payloadLen = offset;
|
||||
|
||||
sendFrame(frame);
|
||||
}
|
||||
|
||||
|
||||
void decodeControlFrame(FrameStruct frame) {
|
||||
uint8_t index = 0;
|
||||
uint8_t type = frame.payload[index] & 0xF0;
|
||||
if (type == CONTROL_DATA_FLAG_TYPE_NODE_DISCOVER_REQ) {
|
||||
DiscoverRequestPayload discReq;
|
||||
discReq.prefixOnly = frame.payload[index++] & 0x01;
|
||||
|
||||
discReq.typeFilter = frame.payload[index++];
|
||||
|
||||
discReq.tag = frame.payload[index++];
|
||||
discReq.tag |= frame.payload[index++] << 8;
|
||||
discReq.tag |= frame.payload[index++] << 16;
|
||||
discReq.tag |= frame.payload[index++] << 24;
|
||||
|
||||
if (index < frame.payloadLen) {
|
||||
discReq.since = frame.payload[index++];
|
||||
discReq.since |= frame.payload[index++] << 8;
|
||||
discReq.since |= frame.payload[index++] << 16;
|
||||
discReq.since |= frame.payload[index++] << 24;
|
||||
}
|
||||
|
||||
} else if (type == CONTROL_DATA_FLAG_DISCOVER_RESP) {
|
||||
DiscoverResponsePayload discResp;
|
||||
discResp.nodeType = frame.payload[index++] & 0x0F;
|
||||
|
||||
discResp.snr = frame.payload[index++];
|
||||
|
||||
discResp.tag = frame.payload[index++];
|
||||
discResp.tag |= frame.payload[index++] << 8;
|
||||
discResp.tag |= frame.payload[index++] << 16;
|
||||
discResp.tag |= frame.payload[index++] << 24;
|
||||
|
||||
uint8_t remainingLen = frame.payloadLen - index;
|
||||
|
||||
uint8_t pubKeyLen = (remainingLen > 8) ? sizeof(discResp.pubkey) : 8;
|
||||
memcpy(discResp.pubkey, &(frame.payload[index]), pubKeyLen);
|
||||
index += pubKeyLen;
|
||||
|
||||
}
|
||||
}
|
||||
8
User/meshcore/packets/control.h
Normal file
8
User/meshcore/packets/control.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef CONTROL_HEADER
|
||||
#define CONTROL_HEADER
|
||||
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
void decodeControlFrame(FrameStruct frame);
|
||||
|
||||
#endif
|
||||
3
User/meshcore/packets/custom.c
Normal file
3
User/meshcore/packets/custom.c
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
#define TAG "Custom"
|
||||
6
User/meshcore/packets/custom.h
Normal file
6
User/meshcore/packets/custom.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef CUSTOM_HEADER
|
||||
#define CUSTOM_HEADER
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
309
User/meshcore/packets/encrypted.c
Normal file
309
User/meshcore/packets/encrypted.c
Normal file
@@ -0,0 +1,309 @@
|
||||
#include "lib/config.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include "meshcore/packets/ack.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "meshcore/stats.h"
|
||||
#include "util/hexdump.h"
|
||||
#include "util/log.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "encrypted.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
|
||||
#define TAG "EncryptedMessage"
|
||||
|
||||
void sendEncryptedFrame (NodeEntry *targetNode, uint8_t payloadType, const uint8_t *plain, size_t plainLen) {
|
||||
FrameStruct frame;
|
||||
uint8_t offset = 0;
|
||||
|
||||
// 1. Header
|
||||
frame.header =
|
||||
ROUTE_TYPE_FLOOD | //currently flood
|
||||
payloadType |
|
||||
PAYLOAD_VERSION_0;
|
||||
|
||||
// 2. Destination + source
|
||||
frame.payload[offset++] = targetNode->pubKey[0];
|
||||
frame.payload[offset++] = persistent.pubkey[0];
|
||||
|
||||
// 4. Encrypt + MAC
|
||||
size_t encLen;
|
||||
encrypt_then_mac (
|
||||
targetNode->secret,
|
||||
32,
|
||||
plain,
|
||||
plainLen,
|
||||
frame.payload + offset,
|
||||
&encLen);
|
||||
|
||||
offset += encLen;
|
||||
|
||||
// 5. Finalize
|
||||
frame.payloadLen = offset;
|
||||
memcpy (&frame.path, &targetNode->path, sizeof (frame.path));
|
||||
|
||||
hexdump ("Encrypted frame", frame.payload, frame.payloadLen);
|
||||
sendFrame (frame);
|
||||
}
|
||||
|
||||
void sendEncryptedTextMessage (NodeEntry *targetNode, const PlainTextMessagePayload *msg) {
|
||||
if (targetNode == NULL) {
|
||||
ESP_LOGW(TAG, "Node is null");
|
||||
return;
|
||||
}
|
||||
if (targetNode->last_seen_lt == 0) {
|
||||
ESP_LOGW(TAG, "Node is not populated");
|
||||
return;
|
||||
}
|
||||
uint8_t buf[256];
|
||||
uint8_t index = 0;
|
||||
|
||||
uint8_t msgLen = strlen(msg->message) + 1;
|
||||
buf[index++] = msg->timestamp;
|
||||
buf[index++] = msg->timestamp >> 8;
|
||||
buf[index++] = msg->timestamp >> 16;
|
||||
buf[index++] = msg->timestamp >> 24;
|
||||
|
||||
buf[index++] = (msg->textType << 2) | (msg->attempt & 0x03);
|
||||
memcpy (&buf[index], msg->message, msgLen);
|
||||
|
||||
sendEncryptedFrame (
|
||||
targetNode,
|
||||
PAYLOAD_TYPE_TXT_MSG,
|
||||
buf,
|
||||
index + msgLen);
|
||||
}
|
||||
|
||||
void sendEncryptedResponse (NodeEntry *targetNode, const Response *resp) {
|
||||
uint8_t buf[256];
|
||||
uint8_t index = 0;
|
||||
|
||||
buf[index++] = resp->tag;
|
||||
buf[index++] = resp->tag >> 8;
|
||||
buf[index++] = resp->tag >> 16;
|
||||
buf[index++] = resp->tag >> 24;
|
||||
|
||||
memcpy (&buf[index], resp->data, resp->dataLen);
|
||||
|
||||
sendEncryptedFrame (
|
||||
targetNode,
|
||||
PAYLOAD_TYPE_RESPONSE,
|
||||
buf,
|
||||
index);
|
||||
}
|
||||
|
||||
void sendEncryptedRequest (NodeEntry *targetNode, const Request *req) {
|
||||
uint8_t buf[256];
|
||||
uint8_t index = 0;
|
||||
|
||||
buf[index++] = req->timestamp;
|
||||
buf[index++] = req->timestamp >> 8;
|
||||
buf[index++] = req->timestamp >> 16;
|
||||
buf[index++] = req->timestamp >> 24;
|
||||
|
||||
buf[index++] = req->requestType;
|
||||
memcpy (&buf[index], req->data, req->dataLen);
|
||||
|
||||
sendEncryptedFrame (
|
||||
targetNode,
|
||||
PAYLOAD_TYPE_REQ,
|
||||
buf,
|
||||
index);
|
||||
}
|
||||
|
||||
void sendEncryptedPathPayload (NodeEntry *targetNode, const ReturnedPathPayload *path) {
|
||||
uint8_t buf[256];
|
||||
uint8_t index = 0;
|
||||
|
||||
buf[index++] = path->path.pathLen;
|
||||
memcpy (&buf[index], path->path.path, path->path.pathLen);
|
||||
index += path->path.pathLen;
|
||||
|
||||
buf[index++] = path->extra.type;
|
||||
memcpy (&buf[index], path->extra.data,
|
||||
sizeof (path->extra.data));
|
||||
|
||||
sendEncryptedFrame (
|
||||
targetNode,
|
||||
PAYLOAD_TYPE_PATH,
|
||||
buf,
|
||||
index);
|
||||
}
|
||||
|
||||
void printRequest (const Request *req) {
|
||||
printf ("Request:\n");
|
||||
printf (" Timestamp: %u\n", req->timestamp);
|
||||
printf (" Type: 0x%02X\n", req->requestType);
|
||||
printf (" Data: ");
|
||||
for (int i = 0; i < req->dataLen; i++) {
|
||||
printf ("%02X ", req->data[i]);
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
void printResponse (const Response *resp) {
|
||||
printf ("Response:\n");
|
||||
printf (" Tag: %u\n", resp->tag);
|
||||
printf (" Data: ");
|
||||
for (int i = 0; i < resp->dataLen; i++) {
|
||||
printf ("%02X ", resp->data[i]);
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
void printPlainTextMessage (const PlainTextMessagePayload *msg) {
|
||||
printf ("PlainTextMessage:\n");
|
||||
printf (" Timestamp: %u\n", msg->timestamp);
|
||||
printf (" Attempt: %u\n", msg->attempt);
|
||||
printf (" TextType: %u\n", msg->textType);
|
||||
printf (" Message: %.*s\n", (int)strlen (msg->message), msg->message);
|
||||
}
|
||||
|
||||
void printReturnedPathPayload (const ReturnedPathPayload *path) {
|
||||
printf ("ReturnedPathPayload:\n");
|
||||
printf (" Path Length: %u\n", path->path.pathLen);
|
||||
printf (" Path: ");
|
||||
for (int i = 0; i < path->path.pathLen; i++) {
|
||||
printf ("%02X ", path->path.path[i]);
|
||||
}
|
||||
printf ("\n");
|
||||
printf (" Extra Type: %u\n", path->extra.type);
|
||||
printf (" Extra Data: ");
|
||||
for (int i = 0; i < sizeof (path->extra.data); i++) {
|
||||
printf ("%02X ", path->extra.data[i]);
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
void printEncryptedPayload (const EncryptedPayloadStruct *enc) {
|
||||
printf ("EncryptedPayload:\n");
|
||||
printf (" Type: 0x%02X\n", enc->type);
|
||||
printf (" DestinationHash: 0x%02X\n", enc->destinationHash);
|
||||
printf (" SourceHash: 0x%02X\n", enc->sourceHash);
|
||||
printf (" CipherMAC: 0x%04X\n", enc->cipherMAC);
|
||||
printf (" PayloadLen: %zu\n", enc->payloadLen);
|
||||
printf (" Payload: ");
|
||||
for (size_t i = 0; i < enc->payloadLen; i++) {
|
||||
printf ("%02X ", enc->payload[i]);
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
EncryptedPayloadStruct decodeEncryptedPayload (FrameStruct frame) {
|
||||
EncryptedPayloadStruct enc;
|
||||
memset (&enc, 0, sizeof (enc));
|
||||
enc.type = frame.header & PAYLOAD_TYPE_MASK;
|
||||
unsigned char index = 0;
|
||||
|
||||
enc.destinationHash = frame.payload[index++];
|
||||
enc.sourceHash = frame.payload[index++];
|
||||
enc.cipherMAC = frame.payload[index];
|
||||
enc.cipherMAC |= frame.payload[index + 1] << 8;
|
||||
|
||||
if (enc.destinationHash != persistent.pubkey[0]) {
|
||||
return enc;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Finding remote node, sourceHash is %d", enc.sourceHash);
|
||||
|
||||
NodeEntry *remNode = getNode (enc.sourceHash);
|
||||
|
||||
enc.remNode = remNode;
|
||||
|
||||
|
||||
if (remNode == NULL) {
|
||||
ESP_LOGW(TAG, "Node not in DB");
|
||||
return enc;
|
||||
}
|
||||
ESP_LOGI(TAG, "Found node with index %d", remNode - persistent.contacts);
|
||||
|
||||
|
||||
if (mac_then_decrypt (remNode->secret, 32, &(frame.payload[index]), frame.payloadLen - index, enc.payload) != 0) {
|
||||
ESP_LOGW (TAG, "HMAC failed on encrypted message %s", remNode->name);
|
||||
} else {
|
||||
enc.payloadLen = frame.payloadLen - HMAC_SIZE;
|
||||
ESP_LOGI(TAG, "HMAC success from %s, %u bytes long", remNode->name, enc.payloadLen);
|
||||
sendDiscreteAck(enc.payload, 5 + strlen((char *)&enc.payload[5]), remNode->pubKey);
|
||||
}
|
||||
|
||||
return enc;
|
||||
}
|
||||
|
||||
void parseEncryptedPayload (EncryptedPayloadStruct enc) {
|
||||
// printEncryptedPayload(&enc);
|
||||
|
||||
printf ("EncryptedPayload:\n");
|
||||
printf (" Type: 0x%02X\n", enc.type);
|
||||
printf (" DestinationHash: 0x%02X\n", enc.destinationHash);
|
||||
printf (" SourceHash: 0x%02X\n", enc.sourceHash);
|
||||
printf (" CipherMAC: 0x%04X\n", enc.cipherMAC);
|
||||
printf (" PayloadLen: %u\n", enc.payloadLen);
|
||||
printf (" Payload: ");
|
||||
hexdump("Full payload buffer", enc.payload, sizeof(enc.payload));
|
||||
printf ("\n");
|
||||
|
||||
uint8_t index = 0;
|
||||
if (enc.type == PAYLOAD_TYPE_PATH) {
|
||||
|
||||
ReturnedPathPayload retPath;
|
||||
retPath.path.pathLen = enc.payload[index++];
|
||||
if (retPath.path.pathLen > 64) {
|
||||
ESP_LOGW (TAG, "Path too long\n");
|
||||
return;
|
||||
}
|
||||
memcpy (retPath.path.path, &(enc.payload[index]), retPath.path.pathLen);
|
||||
index += retPath.path.pathLen;
|
||||
retPath.extra.type = enc.payload[index++];
|
||||
memcpy (retPath.extra.data, &(enc.payload[index]), enc.payloadLen - index);
|
||||
|
||||
} else if (enc.type == PAYLOAD_TYPE_REQ) {
|
||||
Request req;
|
||||
req.timestamp = enc.payload[index++];
|
||||
req.timestamp |= enc.payload[index++] << 8;
|
||||
req.timestamp |= enc.payload[index++] << 16;
|
||||
req.timestamp |= enc.payload[index++] << 24;
|
||||
req.requestType = enc.payload[index++];
|
||||
req.dataLen = enc.payloadLen - index;
|
||||
memcpy (req.data, &(enc.payload[index]), req.dataLen);
|
||||
printRequest (&req);
|
||||
switch (req.requestType) {
|
||||
Response resp;
|
||||
resp.tag = RTC_GetCounter();
|
||||
memcpy(resp.data, &stats, sizeof(stats));
|
||||
resp.dataLen = sizeof(stats);
|
||||
sendEncryptedResponse(enc.remNode, &resp);
|
||||
break;
|
||||
case REQUEST_KEEPALIVE:
|
||||
break;
|
||||
case REQUEST_GET_TELEMETRY_DATA:
|
||||
break;
|
||||
case REQUEST_GET_MIN_MAX_AVG:
|
||||
break;
|
||||
case REQUEST_GET_ACCESS_LIST:
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (enc.type == PAYLOAD_TYPE_RESPONSE) {
|
||||
Response resp;
|
||||
resp.tag = enc.payload[index++];
|
||||
resp.tag |= enc.payload[index++] << 8;
|
||||
resp.tag |= enc.payload[index++] << 16;
|
||||
resp.tag |= enc.payload[index++] << 24;
|
||||
resp.dataLen = enc.payloadLen - index;
|
||||
memcpy (resp.data, &(enc.payload[index]), resp.dataLen);
|
||||
printResponse (&resp);
|
||||
|
||||
} else if (enc.type == PAYLOAD_TYPE_TXT_MSG) {
|
||||
PlainTextMessagePayload plaintext;
|
||||
plaintext.timestamp = enc.payload[index++];
|
||||
plaintext.timestamp |= enc.payload[index++] << 8;
|
||||
plaintext.timestamp |= enc.payload[index++] << 16;
|
||||
plaintext.timestamp |= enc.payload[index++] << 24;
|
||||
plaintext.attempt = enc.payload[index] & 0x03;
|
||||
plaintext.textType = enc.payload[index++] >> 2;
|
||||
memcpy (plaintext.message, &(enc.payload[index]), enc.payloadLen - index);
|
||||
printPlainTextMessage (&plaintext);
|
||||
}
|
||||
}
|
||||
25
User/meshcore/packets/encrypted.h
Normal file
25
User/meshcore/packets/encrypted.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef ENCRYPTED_HEADER
|
||||
#define ENCRYPTED_HEADER
|
||||
|
||||
#include "lib/config.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "util/log.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
void sendEncryptedFrame (NodeEntry *targetNode, uint8_t payloadType, const uint8_t *plain, size_t plainLen);
|
||||
|
||||
void sendEncryptedTextMessage (NodeEntry *targetNode, const PlainTextMessagePayload *msg);
|
||||
|
||||
void sendEncryptedResponse (NodeEntry *targetNode, const Response *resp);
|
||||
|
||||
void sendEncryptedRequest (NodeEntry *targetNode, const Request *req);
|
||||
|
||||
void sendEncryptedPathPayload (NodeEntry *targetNode, const ReturnedPathPayload *path);
|
||||
|
||||
EncryptedPayloadStruct decodeEncryptedPayload (FrameStruct frame);
|
||||
|
||||
void parseEncryptedPayload (EncryptedPayloadStruct enc);
|
||||
|
||||
#endif
|
||||
108
User/meshcore/packets/group.c
Normal file
108
User/meshcore/packets/group.c
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "ch32v30x_rtc.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "group.h"
|
||||
#include "lib/config.h"
|
||||
#include "util/hexdump.h"
|
||||
#include "util/log.h"
|
||||
#include "string.h"
|
||||
|
||||
#define TAG "GroupMessage"
|
||||
|
||||
void sendGroupMessage (GroupTextMessage msg) {
|
||||
|
||||
msg.channelHash = persistent.aesKeys[msg.keyIndex][0];
|
||||
|
||||
msg.flags = 0;
|
||||
msg.timestamp = RTC_GetCounter();
|
||||
|
||||
FrameStruct frame;
|
||||
frame.header = ROUTE_TYPE_FLOOD | PAYLOAD_TYPE_GRP_TXT | PAYLOAD_VERSION_0;
|
||||
frame.path.pathLen = 0;
|
||||
size_t offset = 0;
|
||||
memset (frame.payload, 0, sizeof (frame.payload));
|
||||
frame.payload[offset++] = msg.channelHash;
|
||||
|
||||
uint8_t cipherBuf[176];
|
||||
size_t offset2 = 0;
|
||||
memcpy (cipherBuf, (const void *)&(msg.timestamp), 4);
|
||||
offset2 += 4;
|
||||
cipherBuf[offset2++] = msg.flags;
|
||||
size_t textSize = offset2 + strlen ((const char *)msg.text);
|
||||
if (textSize > 175) {
|
||||
textSize = 175;
|
||||
}
|
||||
memcpy (cipherBuf + offset2, msg.text, textSize);
|
||||
offset2 += textSize;
|
||||
|
||||
size_t olen = 0;
|
||||
hexdump ("TxDumpDec", cipherBuf, offset2);
|
||||
encrypt_then_mac (&(persistent.aesKeys[msg.keyIndex][1]), 16, cipherBuf, offset2, &(frame.payload[offset]), &olen);
|
||||
|
||||
frame.payloadLen = olen + 1;
|
||||
|
||||
sendFrame (frame);
|
||||
return;
|
||||
}
|
||||
|
||||
void makeSendGroupMessage (char *txt, uint8_t keyIndex) {
|
||||
GroupTextMessage msg;
|
||||
strcpy((char *) msg.text, persistent.nodeName);
|
||||
strcat ((char *)msg.text, ": ");
|
||||
strcat ((char *)msg.text, txt);
|
||||
msg.keyIndex = keyIndex;
|
||||
sendGroupMessage (msg);
|
||||
return;
|
||||
}
|
||||
|
||||
GroupTextMessage decodeGroupMessage (FrameStruct frame) {
|
||||
GroupTextMessage msg;
|
||||
memset (&msg, 0, sizeof (msg));
|
||||
if ((frame.header & PAYLOAD_TYPE_MASK) != PAYLOAD_TYPE_GRP_TXT) {
|
||||
ESP_LOGW (TAG, "Not a group text");
|
||||
return msg;
|
||||
}
|
||||
unsigned char index = 0;
|
||||
msg.channelHash = frame.payload[index++];
|
||||
unsigned char tmp[184];
|
||||
|
||||
|
||||
unsigned char decrypted = 0;
|
||||
for (unsigned char i = 0; i < AESKeyCount; i++) {
|
||||
|
||||
if (msg.channelHash != persistent.aesKeys[i][0]) {
|
||||
ESP_LOGW (TAG, "Hash %d does not equal %d", persistent.aesKeys[i][0], msg.channelHash);
|
||||
continue;
|
||||
}
|
||||
|
||||
ESP_LOGW (TAG, "Hash does equal %d", msg.channelHash);
|
||||
|
||||
if (mac_then_decrypt (persistent.aesKeys[i] + 1, 16, frame.payload + index, frame.payloadLen - index, tmp) != 0) {
|
||||
ESP_LOGW (TAG, "HMAC failed on grouphash key %d not matching %d", persistent.aesKeys[i][0], msg.channelHash);
|
||||
continue;
|
||||
}
|
||||
hexdump ("RxDumpDec", tmp, frame.payloadLen - index);
|
||||
decrypted = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!decrypted) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
unsigned char plaintextLen = frame.payloadLen - index;
|
||||
index = 0;
|
||||
|
||||
memcpy (&msg.timestamp, tmp + index, 4);
|
||||
index += 4;
|
||||
msg.flags = tmp[index++];
|
||||
|
||||
memcpy (msg.text, tmp + index, plaintextLen - index);
|
||||
return msg;
|
||||
}
|
||||
|
||||
void printGroupMessage (GroupTextMessage msg) {
|
||||
printf ("Message with channel hash %d, flags %d: %s\n", msg.channelHash,
|
||||
msg.flags, msg.text);
|
||||
}
|
||||
15
User/meshcore/packets/group.h
Normal file
15
User/meshcore/packets/group.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef GROUP_HEADER
|
||||
#define GROUP_HEADER
|
||||
|
||||
#include "stdint.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
void sendGroupMessage (GroupTextMessage msg);
|
||||
|
||||
void makeSendGroupMessage (char *txt, uint8_t keyIndex);
|
||||
|
||||
GroupTextMessage decodeGroupMessage (FrameStruct frame);
|
||||
|
||||
void printGroupMessage (GroupTextMessage msg);
|
||||
|
||||
#endif
|
||||
4
User/meshcore/packets/multipart.c
Normal file
4
User/meshcore/packets/multipart.c
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "multipart.h"
|
||||
|
||||
#define TAG "Multipart"
|
||||
4
User/meshcore/packets/multipart.h
Normal file
4
User/meshcore/packets/multipart.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#ifndef MULTIPART_HEADER
|
||||
#define MULTIPART_HEADER
|
||||
|
||||
#endif
|
||||
4
User/meshcore/packets/trace.c
Normal file
4
User/meshcore/packets/trace.c
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define TAG "Trace"
|
||||
6
User/meshcore/packets/trace.h
Normal file
6
User/meshcore/packets/trace.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef TRACE_HEADER
|
||||
#define TRACE_HEADER
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -6,6 +6,21 @@
|
||||
#define PAYLOAD_TYPE_MASK 0x3C
|
||||
#define PAYLOAD_VERSION_MASK 0xC0
|
||||
|
||||
#define DONT_RETRANSMIT_HEADER 0xFF
|
||||
|
||||
#define RESP_SERVER_LOGIN_OK 0
|
||||
|
||||
#define FIRMWARE_VER_LEVEL 1
|
||||
|
||||
#define PERM_ACL_ROLE_MASK 3 // lower 2 bits
|
||||
#define PERM_ACL_GUEST 0
|
||||
#define PERM_ACL_READ_ONLY 1
|
||||
#define PERM_ACL_READ_WRITE 2
|
||||
#define PERM_ACL_ADMIN 3
|
||||
|
||||
|
||||
#define MAX_PATH_LEN 64
|
||||
|
||||
typedef enum RouteType {
|
||||
ROUTE_TYPE_TRANSPORT_FLOOD = 0x00,
|
||||
ROUTE_TYPE_FLOOD = 0x01,
|
||||
@@ -13,7 +28,6 @@ typedef enum RouteType {
|
||||
ROUTE_TYPE_TRANSPORT_DIRECT = 0x03,
|
||||
} RouteType;
|
||||
|
||||
|
||||
typedef enum PayloadType {
|
||||
PAYLOAD_TYPE_REQ = 0x00 << 2,
|
||||
PAYLOAD_TYPE_RESPONSE = 0x01 << 2,
|
||||
@@ -26,9 +40,31 @@ typedef enum PayloadType {
|
||||
PAYLOAD_TYPE_PATH = 0x08 << 2,
|
||||
PAYLOAD_TYPE_TRACE = 0x09 << 2,
|
||||
PAYLOAD_TYPE_MULTIPART = 0x0A << 2,
|
||||
PAYLOAD_TYPE_CONTROL = 0x0B << 2,
|
||||
PAYLOAD_TYPE_RAW_CUSTOM = 0x0F << 2,
|
||||
} PayloadType;
|
||||
|
||||
typedef struct DiscoverRequestPayload {
|
||||
uint8_t prefixOnly;
|
||||
uint8_t typeFilter;
|
||||
uint32_t tag;
|
||||
uint32_t since;
|
||||
} DiscoverRequestPayload;
|
||||
|
||||
typedef struct DiscoverResponsePayload {
|
||||
uint8_t nodeType;
|
||||
int8_t snr;
|
||||
uint32_t tag;
|
||||
uint8_t pubkey[32];
|
||||
} DiscoverResponsePayload;
|
||||
|
||||
typedef struct AnonymousRequestPayload {
|
||||
uint8_t destinationHash;
|
||||
uint8_t pubKey[32];
|
||||
uint16_t cipherMAC;
|
||||
uint8_t payloadLen;
|
||||
uint8_t payload[149];
|
||||
} AnonymousRequestPayload;
|
||||
|
||||
typedef enum PayloadVersion {
|
||||
PAYLOAD_VERSION_0 = 0 << 6,
|
||||
@@ -37,14 +73,17 @@ typedef enum PayloadVersion {
|
||||
PAYLOAD_VERSION_3 = 3 << 6,
|
||||
} PayloadVersion;
|
||||
|
||||
typedef struct Path {
|
||||
uint8_t pathLen;
|
||||
uint8_t path[MAX_PATH_LEN];
|
||||
} Path;
|
||||
|
||||
typedef struct FrameStruct {
|
||||
unsigned char header;
|
||||
unsigned char transportCodes[4];
|
||||
unsigned char pathLen;
|
||||
unsigned char path[64];
|
||||
unsigned char payloadLen;
|
||||
unsigned char payload[184];
|
||||
uint8_t header;
|
||||
uint8_t transportCodes[4];
|
||||
Path path;
|
||||
uint8_t payloadLen;
|
||||
uint8_t payload[184];
|
||||
} FrameStruct;
|
||||
|
||||
typedef enum RequestType {
|
||||
@@ -55,14 +94,50 @@ typedef enum RequestType {
|
||||
REQUEST_GET_ACCESS_LIST = 0x05,
|
||||
} RequestType;
|
||||
|
||||
typedef struct Request {
|
||||
uint32_t timestamp;
|
||||
uint8_t requestType;
|
||||
uint8_t dataLen;
|
||||
uint8_t data[180]; // hopefully correct len
|
||||
} Request;
|
||||
|
||||
typedef struct Response {
|
||||
uint32_t tag; // sender timestamp
|
||||
uint8_t dataLen;
|
||||
uint8_t data[180]; // hopefully correct len
|
||||
} Response;
|
||||
|
||||
typedef struct Node {
|
||||
uint8_t pubKey[32];
|
||||
char name[32];
|
||||
int32_t latitude;
|
||||
int32_t longitude;
|
||||
int32_t lastSeen;
|
||||
char flags;
|
||||
} Node;
|
||||
|
||||
typedef struct EncryptedPayloadStruct {
|
||||
FrameStruct rawFrame;
|
||||
unsigned char destinationHash;
|
||||
unsigned char sourceHash;
|
||||
unsigned char payload[180];
|
||||
uint8_t destinationHash;
|
||||
uint8_t sourceHash;
|
||||
uint16_t cipherMAC;
|
||||
uint8_t payloadLen;
|
||||
uint8_t payload[180];
|
||||
uint8_t type;
|
||||
NodeEntry *remNode;
|
||||
} EncryptedPayloadStruct;
|
||||
|
||||
typedef enum NodeType {
|
||||
NODE_TYPE_CHAT_NODE = 1,
|
||||
NODE_TYPE_REPEATER = 2,
|
||||
NODE_TYPE_ROOM_SERVER = 3,
|
||||
NODE_TYPE_SENSOR = 4
|
||||
} NodeType;
|
||||
|
||||
typedef enum ControlDataFlags {
|
||||
CONTROL_DATA_FLAG_TYPE_NODE_DISCOVER_REQ = 0x80,
|
||||
CONTROL_DATA_FLAG_DISCOVER_RESP = 0x90
|
||||
} ControlDataFlags;
|
||||
|
||||
typedef enum AdvertisementPayloadFlags {
|
||||
ADVERTISEMENT_FLAG_IS_CHAT_NODE = 0x01,
|
||||
ADVERTISEMENT_FLAG_IS_REAPEATER = 0x02,
|
||||
@@ -75,46 +150,58 @@ typedef enum AdvertisementPayloadFlags {
|
||||
} AdvertisementPayloadFlags;
|
||||
|
||||
typedef struct AdvertisementPayload {
|
||||
unsigned char pubKey[32];
|
||||
uint8_t pubKey[32];
|
||||
int32_t timestamp;
|
||||
unsigned char signature[64];
|
||||
unsigned char dataFlags;
|
||||
uint8_t signature[64];
|
||||
uint8_t dataFlags;
|
||||
int32_t latitude;
|
||||
int32_t longitude;
|
||||
int16_t rfu1;
|
||||
int16_t rfu2;
|
||||
char nodeName[128];
|
||||
char nodeName[32];
|
||||
|
||||
} AdvertisementPayload;
|
||||
|
||||
typedef struct ReturnedPathPayload {
|
||||
unsigned char destinationHash;
|
||||
unsigned char sourceHash;
|
||||
unsigned char pathLen;
|
||||
typedef struct Extra {
|
||||
uint8_t type;
|
||||
uint8_t data[180]; // hopefully long enough
|
||||
} Extra;
|
||||
|
||||
typedef struct ReturnedPathPayload {
|
||||
Path path;
|
||||
Extra extra;
|
||||
} ReturnedPathPayload;
|
||||
|
||||
typedef struct RequestPayload {
|
||||
unsigned char destinationHash;
|
||||
unsigned char sourceHash;
|
||||
} RequestPayload;
|
||||
|
||||
typedef struct ResponsePayload {
|
||||
unsigned char destinationHash;
|
||||
unsigned char sourceHash;
|
||||
} ResponsePayload;
|
||||
|
||||
typedef struct PlainTextMessagePayload {
|
||||
unsigned char destinationHash;
|
||||
unsigned char sourceHash;
|
||||
uint32_t timestamp;
|
||||
uint8_t textType;
|
||||
uint8_t attempt;
|
||||
uint8_t message[180]; // hopefully long enough
|
||||
} PlainTextMessagePayload;
|
||||
|
||||
typedef struct GroupTextMessage {
|
||||
unsigned char channelHash;
|
||||
unsigned char keyIndex;
|
||||
uint8_t channelHash;
|
||||
uint8_t keyIndex;
|
||||
int32_t timestamp;
|
||||
unsigned char flags;
|
||||
unsigned char text[190];
|
||||
uint8_t flags;
|
||||
uint8_t text[190];
|
||||
} GroupTextMessage;
|
||||
|
||||
typedef struct RepeaterStats {
|
||||
uint16_t millivolts;
|
||||
uint16_t txQueueLength;
|
||||
int16_t noiseFloor;
|
||||
int16_t lastRSSI;
|
||||
uint32_t packetsReceivedCount;
|
||||
uint32_t packetsSentCount;
|
||||
uint32_t totalAirTimeSeconds;
|
||||
uint32_t totalUpTimeSeconds;
|
||||
uint32_t sentFloodCount, sentDirectCount;
|
||||
uint32_t receivedFloodCount, receivedDirectCount;
|
||||
uint16_t err_events; // was 'n_full_events'
|
||||
int16_t lastSNR; // x 4
|
||||
uint16_t n_direct_dups, n_flood_dups;
|
||||
uint32_t total_rx_air_time_secs;
|
||||
} RepeaterStats;
|
||||
|
||||
#endif
|
||||
|
||||
3
User/meshcore/stats.c
Normal file
3
User/meshcore/stats.c
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "stats.h"
|
||||
|
||||
RepeaterStats stats;
|
||||
7
User/meshcore/stats.h
Normal file
7
User/meshcore/stats.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef STATS_HEADER
|
||||
#define STATS_HEADER
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
extern RepeaterStats stats;
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user