2026-10-12 20:29:29 +02:00

116 lines
3.9 KiB
C++

#include "doctest.h"
#include <mesh_meshtastic/meshtastic_protocol.h>
#include <cstring>
namespace {
MeshtasticChannelKey defaultKey() {
const uint8_t psk = 1;
MeshtasticChannelKey key {};
REQUIRE(meshtastic_expand_psk(&psk, 1, &key));
return key;
}
} // namespace
TEST_CASE("Meshtastic default channel matches the known LongFast values") {
const auto key = defaultKey();
CHECK_EQ(key.length, 16);
CHECK_EQ(memcmp(key.bytes, MESHTASTIC_DEFAULT_KEY, 16), 0);
// LongFast with the default key is channel hash 8
CHECK_EQ(meshtastic_channel_hash("LongFast", key), 0x08);
// US LongFast sits at 906.875 MHz, EU_868 has a single slot
CHECK_EQ(meshtastic_channel_frequency("LongFast", 902000000, 928000000, 250000), 906875000);
CHECK_EQ(meshtastic_channel_frequency("LongFast", 869400000, 869650000, 250000), 869525000);
}
TEST_CASE("Meshtastic PSK expansion") {
const uint8_t second = 2;
MeshtasticChannelKey key {};
REQUIRE(meshtastic_expand_psk(&second, 1, &key));
CHECK_EQ(key.bytes[15], MESHTASTIC_DEFAULT_KEY[15] + 1);
const uint8_t none = 0;
CHECK_FALSE(meshtastic_expand_psk(&none, 1, &key));
uint8_t long_key[32] = {};
CHECK(meshtastic_expand_psk(long_key, 32, &key));
CHECK_EQ(key.length, 32);
CHECK_FALSE(meshtastic_expand_psk(long_key, 20, &key));
}
TEST_CASE("Meshtastic header round trip") {
MeshtasticHeader header;
header.to = 0x11223344;
header.from = 0xAABBCCDD;
header.id = 0x01020304;
header.hopLimit = 2;
header.wantAck = true;
header.hopStart = 3;
header.channelHash = 8;
uint8_t data[MESHTASTIC_HEADER_SIZE];
meshtastic_write_header(header, data);
CHECK_EQ(data[0], 0x44);
CHECK_EQ(data[7], 0xAA);
CHECK_EQ(data[12], 2 | 0x08 | (3 << 5));
MeshtasticHeader parsed;
REQUIRE(meshtastic_read_header(data, sizeof(data), &parsed));
CHECK_EQ(parsed.to, header.to);
CHECK_EQ(parsed.from, header.from);
CHECK_EQ(parsed.id, header.id);
CHECK_EQ(parsed.hopLimit, 2);
CHECK(parsed.wantAck);
CHECK_EQ(parsed.hopStart, 3);
CHECK_EQ(parsed.channelHash, 8);
}
TEST_CASE("Meshtastic Data encoding and AES-CTR round trip") {
MeshtasticData data;
data.port = MeshtasticPort::TextMessage;
const char* text = "hello mesh";
data.payload.assign(text, text + strlen(text));
data.requestId = 0x12345678;
const auto encoded = meshtastic_encode_data(data);
// portnum = 1, then the payload
CHECK_EQ(encoded[0], 0x08);
CHECK_EQ(encoded[1], 0x01);
CHECK_EQ(encoded[2], 0x12);
CHECK_EQ(encoded[3], strlen(text));
const auto key = defaultKey();
uint8_t ciphertext[64];
uint8_t plaintext[64];
REQUIRE(meshtastic_crypt(key, 42, 0xAABBCCDD, encoded.data(), ciphertext, encoded.size()));
CHECK_NE(memcmp(ciphertext, encoded.data(), encoded.size()), 0);
REQUIRE(meshtastic_crypt(key, 42, 0xAABBCCDD, ciphertext, plaintext, encoded.size()));
MeshtasticData decoded;
REQUIRE(meshtastic_decode_data(plaintext, encoded.size(), &decoded));
CHECK_EQ(decoded.port, MeshtasticPort::TextMessage);
CHECK_EQ(std::string(decoded.payload.begin(), decoded.payload.end()), text);
CHECK_EQ(decoded.requestId, 0x12345678);
// Decrypting with another packet id yields garbage
REQUIRE(meshtastic_crypt(key, 43, 0xAABBCCDD, ciphertext, plaintext, encoded.size()));
CHECK_NE(memcmp(plaintext, encoded.data(), encoded.size()), 0);
}
TEST_CASE("Meshtastic User encoding round trip") {
MeshtasticUser user;
user.id = meshtastic_node_id(0xDEADBEEF);
user.longName = "Tactility";
user.shortName = "Tact";
CHECK_EQ(user.id, "!deadbeef");
const auto encoded = meshtastic_encode_user(user);
MeshtasticUser decoded;
REQUIRE(meshtastic_decode_user(encoded.data(), encoded.size(), &decoded));
CHECK_EQ(decoded.id, "!deadbeef");
CHECK_EQ(decoded.longName, "Tactility");
CHECK_EQ(decoded.shortName, "Tact");
}