97 lines
3.7 KiB
C++
97 lines
3.7 KiB
C++
#include "BeaconFrames.h"
|
|
|
|
#if MESH_ENABLE_TX_BEACONS
|
|
|
|
#include "UTF8Helpers.h"
|
|
#include <Utils.h>
|
|
#include <string.h>
|
|
|
|
namespace mesh { namespace beacon {
|
|
|
|
const uint8_t PublicKey[16] = {
|
|
0x8b, 0x33, 0x87, 0xe9, 0xc5, 0xcd, 0xea, 0x6a,
|
|
0xc9, 0xe5, 0xed, 0xba, 0xa1, 0x15, 0xcd, 0x72
|
|
};
|
|
|
|
bool validText(const char* text) {
|
|
if (!text) return false;
|
|
const size_t length = strnlen(text, MaxTextBytes + 1);
|
|
if (!length || length > MaxTextBytes
|
|
|| validUtf8PrefixLength(text, length) != length) return false;
|
|
for (size_t i = 0; i < length; ++i) {
|
|
const uint8_t byte = static_cast<uint8_t>(text[i]);
|
|
if (byte < 32 || byte == 127) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
size_t formatText(const char* node_name, const char* text, char* out, size_t capacity) {
|
|
if (!node_name || !out || !validText(text)) return 0;
|
|
const size_t name_length = strnlen(node_name, MaxNameBytes + 1);
|
|
const size_t text_length = strlen(text);
|
|
if (!name_length || name_length > MaxNameBytes
|
|
|| validUtf8PrefixLength(node_name, name_length) != name_length
|
|
|| capacity < name_length + 2 + text_length + 1) return 0;
|
|
// The name and message use the normal MeshCore visible sender convention.
|
|
// A colon in a configured name must not create a second sender delimiter.
|
|
for (size_t i = 0; i < name_length; ++i) out[i] = node_name[i] == ':' ? ';' : node_name[i];
|
|
out[name_length] = ':';
|
|
out[name_length + 1] = ' ';
|
|
memcpy(out + name_length + 2, text, text_length);
|
|
const size_t length = name_length + 2 + text_length;
|
|
out[length] = 0;
|
|
return length;
|
|
}
|
|
|
|
size_t publicPayload(uint32_t timestamp, const char* node_name, const char* text,
|
|
uint8_t* out, size_t capacity) {
|
|
if (!out || capacity < 6) return 0;
|
|
char formatted[MaxFormattedBytes + 1];
|
|
const size_t length = formatText(node_name, text, formatted, sizeof(formatted));
|
|
if (!length || capacity < 5 + length) return 0;
|
|
for (unsigned i = 0; i < 4; ++i) out[i] = static_cast<uint8_t>(timestamp >> (8 * i));
|
|
out[4] = 0; // TXT_TYPE_PLAIN, no ACK/response requested.
|
|
memcpy(out + 5, formatted, length);
|
|
return 5 + length;
|
|
}
|
|
|
|
size_t meshtextFrame(mt::Preset preset, uint32_t from, uint32_t id, uint8_t hops,
|
|
const char* node_name, const char* text,
|
|
uint8_t out[mt::MaxFrameSize]) {
|
|
#if MESH_ENABLE_MESHTEXT_WIRE
|
|
const char* channel = mt::presetName(preset);
|
|
if (!channel || !out) return 0;
|
|
char formatted[MaxFormattedBytes + 1];
|
|
const size_t length = formatText(node_name, text, formatted, sizeof(formatted));
|
|
return length ? mt::encodeText(from, id, hops, channel, mt::DefaultPsk,
|
|
sizeof(mt::DefaultPsk), formatted, length, out) : 0;
|
|
#else
|
|
(void)preset; (void)from; (void)id; (void)hops;
|
|
(void)node_name; (void)text; (void)out;
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
uint32_t nodeId(const uint8_t public_key[32]) {
|
|
if (!public_key) return 0;
|
|
static const uint8_t domain[] = {'b', 'e', 'a', 'c', 'o', 'n'};
|
|
uint8_t digest[4], chat_digest[4];
|
|
Utils::sha256(digest, sizeof(digest), public_key, 32, domain, sizeof(domain));
|
|
Utils::sha256(chat_digest, sizeof(chat_digest), public_key, 32);
|
|
const auto number = [](const uint8_t bytes[4]) {
|
|
return uint32_t(bytes[0]) | (uint32_t(bytes[1]) << 8)
|
|
| (uint32_t(bytes[2]) << 16) | (uint32_t(bytes[3]) << 24);
|
|
};
|
|
uint32_t value = number(digest), chat = number(chat_digest);
|
|
if (value < 4 || value == UINT32_MAX) value = 0x0badf00d;
|
|
if (chat < 4 || chat == UINT32_MAX) chat = 0x0badf00d;
|
|
// Domain separation avoids systematic reuse. Also cover a truncated-hash
|
|
// collision with the default Companion identity without changing its ID.
|
|
if (value == chat) value = value == UINT32_MAX - 1 ? 4 : value + 1;
|
|
return value;
|
|
}
|
|
|
|
} } // namespace mesh::beacon
|
|
|
|
#endif // MESH_ENABLE_TX_BEACONS
|