|
|
@ -1,12 +1,22 @@ |
|
|
#include <cstddef> |
|
|
#include <cstddef> |
|
|
#include <cstdint> |
|
|
#include <cstdint> |
|
|
|
|
|
#include <cstring> |
|
|
|
|
|
#include <optional> |
|
|
|
|
|
|
|
|
#include <gtest/gtest.h> |
|
|
#include <gtest/gtest.h> |
|
|
|
|
|
|
|
|
#include "helpers/AdvertDataHelpers.h" |
|
|
#include "helpers/BaseChatMesh.h" |
|
|
|
|
|
#include "helpers/SimpleMeshTables.h" |
|
|
|
|
|
|
|
|
namespace { |
|
|
namespace { |
|
|
|
|
|
|
|
|
|
|
|
constexpr char kSenderPrivateKeyHex[] = |
|
|
|
|
|
"70" |
|
|
|
|
|
"65e18fd9fabb70c1ed90dca19907de698c88b709ea146eafd93d9b830c7b60" |
|
|
|
|
|
"c4681193c79bbc39945ba8064104bb618f8fd7a84a0af6f57033d6e8ddcd6471"; |
|
|
|
|
|
constexpr char kSenderPublicKeyHex[] = |
|
|
|
|
|
"1ec77175b0918ed206f9ae04ec136d6d5d4315bb26305427f645b492e9350c10"; |
|
|
|
|
|
|
|
|
void WriteU8(uint8_t* dest, size_t* offset, uint8_t value) { |
|
|
void WriteU8(uint8_t* dest, size_t* offset, uint8_t value) { |
|
|
dest[(*offset)++] = value; |
|
|
dest[(*offset)++] = value; |
|
|
} |
|
|
} |
|
|
@ -20,9 +30,7 @@ void WriteI32Le(uint8_t* dest, size_t* offset, int32_t value) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void WriteBytes(uint8_t* dest, size_t* offset, const uint8_t* bytes, size_t length) { |
|
|
void WriteBytes(uint8_t* dest, size_t* offset, const uint8_t* bytes, size_t length) { |
|
|
for (size_t i = 0; i < length; ++i) { |
|
|
memcpy(dest + *offset, bytes, length); |
|
|
dest[*offset + i] = bytes[i]; |
|
|
|
|
|
} |
|
|
|
|
|
*offset += length; |
|
|
*offset += length; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -32,7 +40,206 @@ void WriteStringLiteral(uint8_t* dest, size_t* offset, const char (&value)[N]) { |
|
|
WriteBytes(dest, offset, reinterpret_cast<const uint8_t*>(value), N - 1); |
|
|
WriteBytes(dest, offset, reinterpret_cast<const uint8_t*>(value), N - 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
TEST(AdvertData, RoundTripsNameOnly) { |
|
|
class FakeMillis final : public mesh::MillisecondClock { |
|
|
|
|
|
public: |
|
|
|
|
|
unsigned long getMillis() override { |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
class FakeRtc final : public mesh::RTCClock { |
|
|
|
|
|
public: |
|
|
|
|
|
explicit FakeRtc(uint32_t initial_time) : _time(initial_time) {} |
|
|
|
|
|
|
|
|
|
|
|
uint32_t getCurrentTime() override { |
|
|
|
|
|
return _time; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void setCurrentTime(uint32_t time) override { |
|
|
|
|
|
_time = time; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
uint32_t _time; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
class FakeRng final : public mesh::RNG { |
|
|
|
|
|
public: |
|
|
|
|
|
void random(uint8_t* dest, size_t sz) override { |
|
|
|
|
|
memset(dest, 0x5A, sz); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
class FakeRadio final : public mesh::Radio { |
|
|
|
|
|
public: |
|
|
|
|
|
int recvRaw(uint8_t*, int) override { |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
uint32_t getEstAirtimeFor(int) override { |
|
|
|
|
|
return 1; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
float packetScore(float, int) override { |
|
|
|
|
|
return 1.0f; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool startSendRaw(const uint8_t*, int) override { |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool isSendComplete() override { |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void onSendFinished() override {} |
|
|
|
|
|
|
|
|
|
|
|
bool isInRecvMode() const override { |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
class NoopPacketManager final : public mesh::PacketManager { |
|
|
|
|
|
public: |
|
|
|
|
|
mesh::Packet* allocNew() override { |
|
|
|
|
|
return nullptr; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void free(mesh::Packet*) override {} |
|
|
|
|
|
|
|
|
|
|
|
void queueOutbound(mesh::Packet*, uint8_t, uint32_t) override {} |
|
|
|
|
|
|
|
|
|
|
|
mesh::Packet* getNextOutbound(uint32_t) override { |
|
|
|
|
|
return nullptr; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int getOutboundCount(uint32_t) const override { |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int getOutboundTotal() const override { |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int getFreeCount() const override { |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
mesh::Packet* getOutboundByIdx(int) override { |
|
|
|
|
|
return nullptr; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
mesh::Packet* removeOutboundByIdx(int) override { |
|
|
|
|
|
return nullptr; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void queueInbound(mesh::Packet*, uint32_t) override {} |
|
|
|
|
|
|
|
|
|
|
|
mesh::Packet* getNextInbound(uint32_t) override { |
|
|
|
|
|
return nullptr; |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
class TestChatMesh final : public BaseChatMesh { |
|
|
|
|
|
public: |
|
|
|
|
|
TestChatMesh(mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, |
|
|
|
|
|
mesh::PacketManager& mgr, mesh::MeshTables& tables) |
|
|
|
|
|
: BaseChatMesh(radio, ms, rng, rtc, mgr, tables) {} |
|
|
|
|
|
|
|
|
|
|
|
mesh::DispatcherAction recv(mesh::Packet* pkt) { |
|
|
|
|
|
return onRecvPacket(pkt); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
std::optional<ContactInfo> discovered_contact; |
|
|
|
|
|
|
|
|
|
|
|
protected: |
|
|
|
|
|
void onDiscoveredContact(ContactInfo& contact, bool, uint8_t, const uint8_t*) override { |
|
|
|
|
|
discovered_contact = contact; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ContactInfo* processAck(const uint8_t*) override { |
|
|
|
|
|
return nullptr; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void onContactPathUpdated(const ContactInfo&) override {} |
|
|
|
|
|
|
|
|
|
|
|
void onMessageRecv(const ContactInfo&, mesh::Packet*, uint32_t, const char*) override {} |
|
|
|
|
|
|
|
|
|
|
|
void onCommandDataRecv(const ContactInfo&, mesh::Packet*, uint32_t, const char*) override {} |
|
|
|
|
|
|
|
|
|
|
|
void onSignedMessageRecv(const ContactInfo&, mesh::Packet*, uint32_t, const uint8_t*, const char*) override {} |
|
|
|
|
|
|
|
|
|
|
|
uint32_t calcFloodTimeoutMillisFor(uint32_t) const override { |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
uint32_t calcDirectTimeoutMillisFor(uint32_t, uint8_t) const override { |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void onSendTimeout() override {} |
|
|
|
|
|
|
|
|
|
|
|
void onChannelMessageRecv(const mesh::GroupChannel&, mesh::Packet*, uint32_t, const char*) override {} |
|
|
|
|
|
|
|
|
|
|
|
uint8_t onContactRequest(const ContactInfo&, uint32_t, const uint8_t*, uint8_t, uint8_t*) override { |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void onContactResponse(const ContactInfo&, const uint8_t*, uint8_t) override {} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
mesh::LocalIdentity MakeSenderIdentity() { |
|
|
|
|
|
return mesh::LocalIdentity(kSenderPrivateKeyHex, kSenderPublicKeyHex); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
mesh::Packet BuildSignedAdvertPacket(const mesh::LocalIdentity& sender, uint32_t timestamp, |
|
|
|
|
|
const uint8_t* app_data, uint8_t app_data_len) { |
|
|
|
|
|
mesh::Packet packet; |
|
|
|
|
|
|
|
|
|
|
|
// Wire header: flood-routed advert packet with no path hashes yet.
|
|
|
|
|
|
packet.header = ROUTE_TYPE_FLOOD | (PAYLOAD_TYPE_ADVERT << PH_TYPE_SHIFT); |
|
|
|
|
|
packet.path_len = 0; |
|
|
|
|
|
|
|
|
|
|
|
int offset = 0; |
|
|
|
|
|
// Sender public key: used by the receiver to identify who signed the advert.
|
|
|
|
|
|
memcpy(&packet.payload[offset], sender.pub_key, PUB_KEY_SIZE); |
|
|
|
|
|
offset += PUB_KEY_SIZE; |
|
|
|
|
|
|
|
|
|
|
|
// Advert timestamp: the sender's monotonic advert time used for replay checks.
|
|
|
|
|
|
memcpy(&packet.payload[offset], ×tamp, sizeof(timestamp)); |
|
|
|
|
|
offset += sizeof(timestamp); |
|
|
|
|
|
|
|
|
|
|
|
// Signature field: filled after the signed message bytes are assembled below.
|
|
|
|
|
|
uint8_t* signature = &packet.payload[offset]; |
|
|
|
|
|
offset += SIGNATURE_SIZE; |
|
|
|
|
|
|
|
|
|
|
|
// Raw advert app_data: arbitrary bytes authored by the test, not by createAdvert().
|
|
|
|
|
|
memcpy(&packet.payload[offset], app_data, app_data_len); |
|
|
|
|
|
offset += app_data_len; |
|
|
|
|
|
packet.payload_len = offset; |
|
|
|
|
|
|
|
|
|
|
|
uint8_t message[PUB_KEY_SIZE + 4 + MAX_ADVERT_DATA_SIZE]; |
|
|
|
|
|
int message_len = 0; |
|
|
|
|
|
memcpy(&message[message_len], sender.pub_key, PUB_KEY_SIZE); |
|
|
|
|
|
message_len += PUB_KEY_SIZE; |
|
|
|
|
|
memcpy(&message[message_len], ×tamp, sizeof(timestamp)); |
|
|
|
|
|
message_len += sizeof(timestamp); |
|
|
|
|
|
memcpy(&message[message_len], app_data, app_data_len); |
|
|
|
|
|
message_len += app_data_len; |
|
|
|
|
|
|
|
|
|
|
|
sender.sign(signature, message, message_len); |
|
|
|
|
|
return packet; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
TEST(AdvertData, ParsesNameOnlyFromNetworkPacket) { |
|
|
|
|
|
FakeRadio radio; |
|
|
|
|
|
FakeMillis ms; |
|
|
|
|
|
FakeRng rng; |
|
|
|
|
|
FakeRtc rtc(1704067200U); |
|
|
|
|
|
NoopPacketManager packet_manager; |
|
|
|
|
|
SimpleMeshTables tables; |
|
|
|
|
|
TestChatMesh mesh(radio, ms, rng, rtc, packet_manager, tables); |
|
|
|
|
|
|
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
size_t offset = 0; |
|
|
size_t offset = 0; |
|
|
|
|
|
|
|
|
@ -41,16 +248,29 @@ TEST(AdvertData, RoundTripsNameOnly) { |
|
|
// name field: raw bytes for "alice", consuming the rest of app_data.
|
|
|
// name field: raw bytes for "alice", consuming the rest of app_data.
|
|
|
WriteStringLiteral(app_data, &offset, "alice"); |
|
|
WriteStringLiteral(app_data, &offset, "alice"); |
|
|
|
|
|
|
|
|
AdvertDataParser parser(app_data, offset); |
|
|
mesh::LocalIdentity sender = MakeSenderIdentity(); |
|
|
|
|
|
mesh::Packet packet = BuildSignedAdvertPacket(sender, 1704067201U, app_data, offset); |
|
|
|
|
|
|
|
|
|
|
|
mesh.recv(&packet); |
|
|
|
|
|
|
|
|
ASSERT_TRUE(parser.isValid()); |
|
|
ASSERT_TRUE(mesh.discovered_contact.has_value()); |
|
|
EXPECT_EQ(ADV_TYPE_CHAT, parser.getType()); |
|
|
EXPECT_EQ(ADV_TYPE_CHAT, mesh.discovered_contact->type); |
|
|
EXPECT_TRUE(parser.hasName()); |
|
|
EXPECT_STREQ("alice", mesh.discovered_contact->name); |
|
|
EXPECT_STREQ("alice", parser.getName()); |
|
|
EXPECT_EQ(1704067201U, mesh.discovered_contact->last_advert_timestamp); |
|
|
EXPECT_FALSE(parser.hasLatLon()); |
|
|
EXPECT_EQ(1704067200U, mesh.discovered_contact->lastmod); |
|
|
|
|
|
EXPECT_EQ(0, mesh.discovered_contact->gps_lat); |
|
|
|
|
|
EXPECT_EQ(0, mesh.discovered_contact->gps_lon); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
TEST(AdvertData, RoundTripsNameAndCoordinates) { |
|
|
TEST(AdvertData, ParsesNameAndCoordinatesFromNetworkPacket) { |
|
|
|
|
|
FakeRadio radio; |
|
|
|
|
|
FakeMillis ms; |
|
|
|
|
|
FakeRng rng; |
|
|
|
|
|
FakeRtc rtc(1704067200U); |
|
|
|
|
|
NoopPacketManager packet_manager; |
|
|
|
|
|
SimpleMeshTables tables; |
|
|
|
|
|
TestChatMesh mesh(radio, ms, rng, rtc, packet_manager, tables); |
|
|
|
|
|
|
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
size_t offset = 0; |
|
|
size_t offset = 0; |
|
|
|
|
|
|
|
|
@ -63,20 +283,27 @@ TEST(AdvertData, RoundTripsNameAndCoordinates) { |
|
|
// name field: raw bytes for "node" after the coordinate fields.
|
|
|
// name field: raw bytes for "node" after the coordinate fields.
|
|
|
WriteStringLiteral(app_data, &offset, "node"); |
|
|
WriteStringLiteral(app_data, &offset, "node"); |
|
|
|
|
|
|
|
|
AdvertDataParser parser(app_data, offset); |
|
|
mesh::LocalIdentity sender = MakeSenderIdentity(); |
|
|
|
|
|
mesh::Packet packet = BuildSignedAdvertPacket(sender, 1704067201U, app_data, offset); |
|
|
|
|
|
|
|
|
ASSERT_TRUE(parser.isValid()); |
|
|
mesh.recv(&packet); |
|
|
EXPECT_EQ(ADV_TYPE_REPEATER, parser.getType()); |
|
|
|
|
|
EXPECT_TRUE(parser.hasName()); |
|
|
ASSERT_TRUE(mesh.discovered_contact.has_value()); |
|
|
EXPECT_STREQ("node", parser.getName()); |
|
|
EXPECT_EQ(ADV_TYPE_REPEATER, mesh.discovered_contact->type); |
|
|
EXPECT_TRUE(parser.hasLatLon()); |
|
|
EXPECT_STREQ("node", mesh.discovered_contact->name); |
|
|
EXPECT_EQ(37774900, parser.getIntLat()); |
|
|
EXPECT_EQ(37774900, mesh.discovered_contact->gps_lat); |
|
|
EXPECT_EQ(-122419400, parser.getIntLon()); |
|
|
EXPECT_EQ(-122419400, mesh.discovered_contact->gps_lon); |
|
|
EXPECT_DOUBLE_EQ(37.7749, parser.getLat()); |
|
|
|
|
|
EXPECT_DOUBLE_EQ(-122.4194, parser.getLon()); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
TEST(AdvertData, RoundTripsCoordinateExtremes) { |
|
|
TEST(AdvertData, ParsesCoordinateExtremesFromNetworkPacket) { |
|
|
|
|
|
FakeRadio radio; |
|
|
|
|
|
FakeMillis ms; |
|
|
|
|
|
FakeRng rng; |
|
|
|
|
|
FakeRtc rtc(1704067200U); |
|
|
|
|
|
NoopPacketManager packet_manager; |
|
|
|
|
|
SimpleMeshTables tables; |
|
|
|
|
|
TestChatMesh mesh(radio, ms, rng, rtc, packet_manager, tables); |
|
|
|
|
|
|
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
size_t offset = 0; |
|
|
size_t offset = 0; |
|
|
|
|
|
|
|
|
@ -89,15 +316,27 @@ TEST(AdvertData, RoundTripsCoordinateExtremes) { |
|
|
// name field: raw bytes for "edge".
|
|
|
// name field: raw bytes for "edge".
|
|
|
WriteStringLiteral(app_data, &offset, "edge"); |
|
|
WriteStringLiteral(app_data, &offset, "edge"); |
|
|
|
|
|
|
|
|
AdvertDataParser parser(app_data, offset); |
|
|
mesh::LocalIdentity sender = MakeSenderIdentity(); |
|
|
|
|
|
mesh::Packet packet = BuildSignedAdvertPacket(sender, 1704067201U, app_data, offset); |
|
|
|
|
|
|
|
|
|
|
|
mesh.recv(&packet); |
|
|
|
|
|
|
|
|
ASSERT_TRUE(parser.isValid()); |
|
|
ASSERT_TRUE(mesh.discovered_contact.has_value()); |
|
|
EXPECT_TRUE(parser.hasLatLon()); |
|
|
EXPECT_EQ(ADV_TYPE_SENSOR, mesh.discovered_contact->type); |
|
|
EXPECT_EQ(-90000000, parser.getIntLat()); |
|
|
EXPECT_STREQ("edge", mesh.discovered_contact->name); |
|
|
EXPECT_EQ(180000000, parser.getIntLon()); |
|
|
EXPECT_EQ(-90000000, mesh.discovered_contact->gps_lat); |
|
|
|
|
|
EXPECT_EQ(180000000, mesh.discovered_contact->gps_lon); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
TEST(AdvertData, RejectsLongitudeOutsideValidRange) { |
|
|
TEST(AdvertData, RejectsLongitudeOutsideValidRangeFromNetworkPacket) { |
|
|
|
|
|
FakeRadio radio; |
|
|
|
|
|
FakeMillis ms; |
|
|
|
|
|
FakeRng rng; |
|
|
|
|
|
FakeRtc rtc(1704067200U); |
|
|
|
|
|
NoopPacketManager packet_manager; |
|
|
|
|
|
SimpleMeshTables tables; |
|
|
|
|
|
TestChatMesh mesh(radio, ms, rng, rtc, packet_manager, tables); |
|
|
|
|
|
|
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
size_t offset = 0; |
|
|
size_t offset = 0; |
|
|
|
|
|
|
|
|
@ -110,12 +349,23 @@ TEST(AdvertData, RejectsLongitudeOutsideValidRange) { |
|
|
// name field: parser should reject before the trailing name matters.
|
|
|
// name field: parser should reject before the trailing name matters.
|
|
|
WriteStringLiteral(app_data, &offset, "node"); |
|
|
WriteStringLiteral(app_data, &offset, "node"); |
|
|
|
|
|
|
|
|
AdvertDataParser parser(app_data, offset); |
|
|
mesh::LocalIdentity sender = MakeSenderIdentity(); |
|
|
|
|
|
mesh::Packet packet = BuildSignedAdvertPacket(sender, 1704067201U, app_data, offset); |
|
|
|
|
|
|
|
|
|
|
|
mesh.recv(&packet); |
|
|
|
|
|
|
|
|
EXPECT_FALSE(parser.isValid()); |
|
|
EXPECT_FALSE(mesh.discovered_contact.has_value()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
TEST(AdvertData, RejectsLongitudeBelowValidRange) { |
|
|
TEST(AdvertData, RejectsLongitudeBelowValidRangeFromNetworkPacket) { |
|
|
|
|
|
FakeRadio radio; |
|
|
|
|
|
FakeMillis ms; |
|
|
|
|
|
FakeRng rng; |
|
|
|
|
|
FakeRtc rtc(1704067200U); |
|
|
|
|
|
NoopPacketManager packet_manager; |
|
|
|
|
|
SimpleMeshTables tables; |
|
|
|
|
|
TestChatMesh mesh(radio, ms, rng, rtc, packet_manager, tables); |
|
|
|
|
|
|
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
size_t offset = 0; |
|
|
size_t offset = 0; |
|
|
|
|
|
|
|
|
@ -128,12 +378,23 @@ TEST(AdvertData, RejectsLongitudeBelowValidRange) { |
|
|
// name field: included to keep the payload shape consistent.
|
|
|
// name field: included to keep the payload shape consistent.
|
|
|
WriteStringLiteral(app_data, &offset, "node"); |
|
|
WriteStringLiteral(app_data, &offset, "node"); |
|
|
|
|
|
|
|
|
AdvertDataParser parser(app_data, offset); |
|
|
mesh::LocalIdentity sender = MakeSenderIdentity(); |
|
|
|
|
|
mesh::Packet packet = BuildSignedAdvertPacket(sender, 1704067201U, app_data, offset); |
|
|
|
|
|
|
|
|
EXPECT_FALSE(parser.isValid()); |
|
|
mesh.recv(&packet); |
|
|
|
|
|
|
|
|
|
|
|
EXPECT_FALSE(mesh.discovered_contact.has_value()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
TEST(AdvertData, RejectsLatitudeOutsideValidRange) { |
|
|
TEST(AdvertData, RejectsLatitudeOutsideValidRangeFromNetworkPacket) { |
|
|
|
|
|
FakeRadio radio; |
|
|
|
|
|
FakeMillis ms; |
|
|
|
|
|
FakeRng rng; |
|
|
|
|
|
FakeRtc rtc(1704067200U); |
|
|
|
|
|
NoopPacketManager packet_manager; |
|
|
|
|
|
SimpleMeshTables tables; |
|
|
|
|
|
TestChatMesh mesh(radio, ms, rng, rtc, packet_manager, tables); |
|
|
|
|
|
|
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
size_t offset = 0; |
|
|
size_t offset = 0; |
|
|
|
|
|
|
|
|
@ -146,12 +407,23 @@ TEST(AdvertData, RejectsLatitudeOutsideValidRange) { |
|
|
// name field: included to keep the payload shape consistent.
|
|
|
// name field: included to keep the payload shape consistent.
|
|
|
WriteStringLiteral(app_data, &offset, "node"); |
|
|
WriteStringLiteral(app_data, &offset, "node"); |
|
|
|
|
|
|
|
|
AdvertDataParser parser(app_data, offset); |
|
|
mesh::LocalIdentity sender = MakeSenderIdentity(); |
|
|
|
|
|
mesh::Packet packet = BuildSignedAdvertPacket(sender, 1704067201U, app_data, offset); |
|
|
|
|
|
|
|
|
|
|
|
mesh.recv(&packet); |
|
|
|
|
|
|
|
|
EXPECT_FALSE(parser.isValid()); |
|
|
EXPECT_FALSE(mesh.discovered_contact.has_value()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
TEST(AdvertData, RejectsLatitudeBelowValidRange) { |
|
|
TEST(AdvertData, RejectsLatitudeBelowValidRangeFromNetworkPacket) { |
|
|
|
|
|
FakeRadio radio; |
|
|
|
|
|
FakeMillis ms; |
|
|
|
|
|
FakeRng rng; |
|
|
|
|
|
FakeRtc rtc(1704067200U); |
|
|
|
|
|
NoopPacketManager packet_manager; |
|
|
|
|
|
SimpleMeshTables tables; |
|
|
|
|
|
TestChatMesh mesh(radio, ms, rng, rtc, packet_manager, tables); |
|
|
|
|
|
|
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
size_t offset = 0; |
|
|
size_t offset = 0; |
|
|
|
|
|
|
|
|
@ -164,9 +436,40 @@ TEST(AdvertData, RejectsLatitudeBelowValidRange) { |
|
|
// name field: included to keep the payload shape consistent.
|
|
|
// name field: included to keep the payload shape consistent.
|
|
|
WriteStringLiteral(app_data, &offset, "node"); |
|
|
WriteStringLiteral(app_data, &offset, "node"); |
|
|
|
|
|
|
|
|
AdvertDataParser parser(app_data, offset); |
|
|
mesh::LocalIdentity sender = MakeSenderIdentity(); |
|
|
|
|
|
mesh::Packet packet = BuildSignedAdvertPacket(sender, 1704067201U, app_data, offset); |
|
|
|
|
|
|
|
|
|
|
|
mesh.recv(&packet); |
|
|
|
|
|
|
|
|
|
|
|
EXPECT_FALSE(mesh.discovered_contact.has_value()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
TEST(AdvertData, RejectsForgedSignatureFromNetworkPacket) { |
|
|
|
|
|
FakeRadio radio; |
|
|
|
|
|
FakeMillis ms; |
|
|
|
|
|
FakeRng rng; |
|
|
|
|
|
FakeRtc rtc(1704067200U); |
|
|
|
|
|
NoopPacketManager packet_manager; |
|
|
|
|
|
SimpleMeshTables tables; |
|
|
|
|
|
TestChatMesh mesh(radio, ms, rng, rtc, packet_manager, tables); |
|
|
|
|
|
|
|
|
|
|
|
uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; |
|
|
|
|
|
size_t offset = 0; |
|
|
|
|
|
|
|
|
|
|
|
// flags/type byte: chat advert with a trailing name field.
|
|
|
|
|
|
WriteU8(app_data, &offset, ADV_TYPE_CHAT | ADV_NAME_MASK); |
|
|
|
|
|
// name field: raw bytes for "mallory".
|
|
|
|
|
|
WriteStringLiteral(app_data, &offset, "mallory"); |
|
|
|
|
|
|
|
|
|
|
|
mesh::LocalIdentity sender = MakeSenderIdentity(); |
|
|
|
|
|
mesh::Packet packet = BuildSignedAdvertPacket(sender, 1704067201U, app_data, offset); |
|
|
|
|
|
|
|
|
|
|
|
// Corrupt the signature bytes after signing so verification must fail in Mesh::onRecvPacket().
|
|
|
|
|
|
packet.payload[PUB_KEY_SIZE + 4] ^= 0xFF; |
|
|
|
|
|
|
|
|
|
|
|
mesh.recv(&packet); |
|
|
|
|
|
|
|
|
EXPECT_FALSE(parser.isValid()); |
|
|
EXPECT_FALSE(mesh.discovered_contact.has_value()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} // namespace
|
|
|
} // namespace
|
|
|
|