#include "Channels.h" #include "PositionPrecision.h" #include "TestUtil.h" #include "mesh-pb-constants.h" #include #include static meshtastic_Position makePosition() { meshtastic_Position position = meshtastic_Position_init_default; position.has_latitude_i = true; position.latitude_i = static_cast(0x12345678); position.has_longitude_i = true; position.longitude_i = static_cast(0x22345678); position.has_altitude = true; position.altitude = 123; position.time = 42; position.location_source = meshtastic_Position_LocSource_LOC_EXTERNAL; position.timestamp = 43; position.sats_in_view = 10; return position; } static meshtastic_Channel makeChannel(meshtastic_Channel_Role role, bool hasModuleSettings, uint32_t positionPrecision) { meshtastic_Channel channel = meshtastic_Channel_init_default; channel.has_settings = true; channel.role = role; channel.settings.has_module_settings = hasModuleSettings; channel.settings.module_settings.position_precision = positionPrecision; return channel; } static void test_applyPositionPrecision_clampsLatLonAndSetsPrecisionBits() { meshtastic_Position position = makePosition(); applyPositionPrecision(position, 16); TEST_ASSERT_EQUAL_INT32(static_cast(0x12348000), position.latitude_i); TEST_ASSERT_EQUAL_INT32(static_cast(0x22348000), position.longitude_i); TEST_ASSERT_EQUAL_UINT32(16, position.precision_bits); TEST_ASSERT_TRUE(position.has_latitude_i); TEST_ASSERT_TRUE(position.has_longitude_i); } static void test_applyPositionPrecision_fullPrecisionKeepsLatLon() { meshtastic_Position position = makePosition(); applyPositionPrecision(position, 32); TEST_ASSERT_EQUAL_INT32(static_cast(0x12345678), position.latitude_i); TEST_ASSERT_EQUAL_INT32(static_cast(0x22345678), position.longitude_i); TEST_ASSERT_EQUAL_UINT32(32, position.precision_bits); } static void test_applyPositionPrecision_zeroScrubsLocationButKeepsTime() { meshtastic_Position position = makePosition(); applyPositionPrecision(position, 0); TEST_ASSERT_FALSE(position.has_latitude_i); TEST_ASSERT_EQUAL_INT32(0, position.latitude_i); TEST_ASSERT_FALSE(position.has_longitude_i); TEST_ASSERT_EQUAL_INT32(0, position.longitude_i); TEST_ASSERT_FALSE(position.has_altitude); TEST_ASSERT_EQUAL_INT32(0, position.altitude); TEST_ASSERT_EQUAL_UINT32(42, position.time); TEST_ASSERT_EQUAL_UINT32(0, position.timestamp); TEST_ASSERT_EQUAL_UINT32(0, position.sats_in_view); TEST_ASSERT_EQUAL_UINT32(0, position.precision_bits); } static void test_applyPositionPrecision_reencodesPositionPacket() { meshtastic_Position position = makePosition(); meshtastic_MeshPacket packet = meshtastic_MeshPacket_init_default; packet.which_payload_variant = meshtastic_MeshPacket_decoded_tag; packet.decoded.portnum = meshtastic_PortNum_POSITION_APP; packet.decoded.payload.size = pb_encode_to_bytes(packet.decoded.payload.bytes, sizeof(packet.decoded.payload.bytes), &meshtastic_Position_msg, &position); TEST_ASSERT_TRUE(applyPositionPrecision(packet, 16)); meshtastic_Position decoded = meshtastic_Position_init_default; TEST_ASSERT_TRUE( pb_decode_from_bytes(packet.decoded.payload.bytes, packet.decoded.payload.size, &meshtastic_Position_msg, &decoded)); TEST_ASSERT_EQUAL_INT32(static_cast(0x12348000), decoded.latitude_i); TEST_ASSERT_EQUAL_INT32(static_cast(0x22348000), decoded.longitude_i); TEST_ASSERT_EQUAL_UINT32(16, decoded.precision_bits); } static void test_getPositionPrecisionForChannel_explicitPrecisionIsHonored() { meshtastic_Channel channel = makeChannel(meshtastic_Channel_Role_PRIMARY, true, 16); TEST_ASSERT_EQUAL_UINT32(16, getPositionPrecisionForChannel(channel)); } static void test_getPositionPrecisionForChannel_explicitZeroDisablesPrimary() { meshtastic_Channel channel = makeChannel(meshtastic_Channel_Role_PRIMARY, true, 0); TEST_ASSERT_EQUAL_UINT32(0, getPositionPrecisionForChannel(channel)); } static void test_getPositionPrecisionForChannel_primaryWithoutModuleSettingsFailsClosed() { // Regression guard for #10509: precision 32 below must be ignored (no module settings). meshtastic_Channel channel = makeChannel(meshtastic_Channel_Role_PRIMARY, false, 32); TEST_ASSERT_EQUAL_UINT32(0, getPositionPrecisionForChannel(channel)); } static void test_getPositionPrecisionForChannel_secondaryWithoutModuleSettingsFailsClosed() { meshtastic_Channel channel = makeChannel(meshtastic_Channel_Role_SECONDARY, false, 32); TEST_ASSERT_EQUAL_UINT32(0, getPositionPrecisionForChannel(channel)); } // End-to-end via the channelIndex overload + live channels singleton, exercising getKey()'s 1-byte->16-byte expansion. static void test_getPositionPrecisionForChannel_clampsPreciseOnDefaultKeyChannel() { channels.initDefaults(); // channel 0: primary, default key (psk {0x01}) -> publicly decryptable uint8_t idx = 0; meshtastic_Channel &ch = channels.getByIndex(idx); ch.settings.has_module_settings = true; ch.settings.module_settings.position_precision = 32; // user requests "Precise" on a public channel TEST_ASSERT_EQUAL_UINT32(MAX_POSITION_PRECISION_PUBLIC_KEY, getPositionPrecisionForChannel(idx)); } static void test_getPositionPrecisionForChannel_keepsPreciseOnStrongKeyChannel() { channels.initDefaults(); uint8_t idx = 0; meshtastic_Channel &ch = channels.getByIndex(idx); memset(ch.settings.psk.bytes, 0xAB, 16); // a private 128-bit key, not the defaultpsk family ch.settings.psk.size = 16; ch.settings.has_module_settings = true; ch.settings.module_settings.position_precision = 32; TEST_ASSERT_EQUAL_UINT32(32, getPositionPrecisionForChannel(idx)); } static CryptoKey makeCryptoKey(const uint8_t *bytes, int length) { CryptoKey k; memset(k.bytes, 0, sizeof(k.bytes)); // CryptoKey::length is int8_t and CryptoKey::bytes is 32 bytes; keep the helper consistent and overflow-safe. int cappedLen = length; if (cappedLen < 0) cappedLen = -1; else if (cappedLen > static_cast(sizeof(k.bytes))) cappedLen = static_cast(sizeof(k.bytes)); if (cappedLen > 0 && bytes != nullptr) { memcpy(k.bytes, bytes, static_cast(cappedLen)); } k.length = static_cast(cappedLen); return k; } static void test_cryptoKeyIsPublic_openKeyIsPublic() { // length 0 == encryption disabled. TEST_ASSERT_TRUE(cryptoKeyIsPublic(makeCryptoKey(nullptr, 0))); } static void test_cryptoKeyIsPublic_defaultKeyIsPublic() { // The expanded default PSK (the 16-byte defaultpsk) -- the case a key-length check misses. TEST_ASSERT_TRUE(cryptoKeyIsPublic(makeCryptoKey(defaultpsk, sizeof(defaultpsk)))); } static void test_cryptoKeyIsPublic_defaultKeyFamilyVariesLastByte() { // Higher indices (e.g. {0x02}) expand to defaultpsk with only the last byte bumped -- still public. uint8_t key[sizeof(defaultpsk)]; memcpy(key, defaultpsk, sizeof(defaultpsk)); key[sizeof(defaultpsk) - 1] = static_cast(key[sizeof(defaultpsk) - 1] + 1); TEST_ASSERT_TRUE(cryptoKeyIsPublic(makeCryptoKey(key, sizeof(key)))); } static void test_cryptoKeyIsPublic_strongKeyIsPrivate() { uint8_t key[16]; memset(key, 0xAB, sizeof(key)); // not the defaultpsk family TEST_ASSERT_FALSE(cryptoKeyIsPublic(makeCryptoKey(key, sizeof(key)))); } static void test_cryptoKeyIsPublic_aes256KeyIsPrivate() { uint8_t key[32]; memset(key, 0x11, sizeof(key)); TEST_ASSERT_FALSE(cryptoKeyIsPublic(makeCryptoKey(key, sizeof(key)))); } static void test_cryptoKeyIsPublic_invalidKeyIsNotPublic() { // length < 0 == no/invalid key (e.g. a disabled channel); it carries no traffic to leak. TEST_ASSERT_FALSE(cryptoKeyIsPublic(makeCryptoKey(nullptr, -1))); } void setUp(void) {} void tearDown(void) {} extern "C" { void setup() { initializeTestEnvironment(); UNITY_BEGIN(); RUN_TEST(test_applyPositionPrecision_clampsLatLonAndSetsPrecisionBits); RUN_TEST(test_applyPositionPrecision_fullPrecisionKeepsLatLon); RUN_TEST(test_applyPositionPrecision_zeroScrubsLocationButKeepsTime); RUN_TEST(test_applyPositionPrecision_reencodesPositionPacket); RUN_TEST(test_getPositionPrecisionForChannel_explicitPrecisionIsHonored); RUN_TEST(test_getPositionPrecisionForChannel_explicitZeroDisablesPrimary); RUN_TEST(test_getPositionPrecisionForChannel_primaryWithoutModuleSettingsFailsClosed); RUN_TEST(test_getPositionPrecisionForChannel_secondaryWithoutModuleSettingsFailsClosed); RUN_TEST(test_getPositionPrecisionForChannel_clampsPreciseOnDefaultKeyChannel); RUN_TEST(test_getPositionPrecisionForChannel_keepsPreciseOnStrongKeyChannel); RUN_TEST(test_cryptoKeyIsPublic_openKeyIsPublic); RUN_TEST(test_cryptoKeyIsPublic_defaultKeyIsPublic); RUN_TEST(test_cryptoKeyIsPublic_defaultKeyFamilyVariesLastByte); RUN_TEST(test_cryptoKeyIsPublic_strongKeyIsPrivate); RUN_TEST(test_cryptoKeyIsPublic_aes256KeyIsPrivate); RUN_TEST(test_cryptoKeyIsPublic_invalidKeyIsNotPublic); exit(UNITY_END()); } void loop() {} }