Skip to content

Instantly share code, notes, and snippets.

@liamcottle
Created September 11, 2024 08:06
Show Gist options
  • Save liamcottle/e85953bccd6a4f8b436ac4284b29af49 to your computer and use it in GitHub Desktop.
Save liamcottle/e85953bccd6a4f8b436ac4284b29af49 to your computer and use it in GitHub Desktop.
#include <MsgPack.h>
size_t maxEncryptedPackageMaxContentLength() {
// LXMF overhead is 111 bytes per message:
// 16 bytes for destination hash
// 16 bytes for source hash
// 64 bytes for Ed25519 signature
// 8 bytes for timestamp
// 7 bytes for msgpack structure
// LXMF_OVERHEAD = 2*DESTINATION_LENGTH + SIGNATURE_LENGTH + 8 + 7
size_t DESTINATION_LENGTH = 16;
size_t LXMF_OVERHEAD = 111;
return RNS::Type::Packet::ENCRYPTED_MDU - LXMF_OVERHEAD + DESTINATION_LENGTH;
}
void sendMessage(RNS::Identity sourceIdentity, RNS::Identity destinationIdentity, const char *content) {
// create destinations (using OUT for source, to avoid registering it, as we just want the hash)
RNS::Destination source = RNS::Destination(sourceIdentity, RNS::Type::Destination::OUT, RNS::Type::Destination::SINGLE, "lxmf", "delivery");
RNS::Destination destination = RNS::Destination(destinationIdentity, RNS::Type::Destination::OUT, RNS::Type::Destination::SINGLE, "lxmf", "delivery");
// create lxmf payload data
const char *title = "";
float timestamp = millis(); // FIXME: this should be a unix timestamp of when message is sent, but we have no clock...
// msgpack the payload
// self.payload = [self.timestamp, self.title, self.content, self.fields]
// NOTE: the payload needs to be unique, otherwise it will have the same hash and be ignored by recipients, so we use millis for timestamp...
MsgPack::Packer packer;
packer.packArraySize(4); // we are msgpacking an array with 4 entries
packer.packFloat(timestamp); // timestamp
packer.packBinary((const uint8_t*)title, strlen(title)); // title (must be sent as bytes, not string)
packer.packBinary((const uint8_t*)content, strlen(content)); // content (must be sent as bytes, not string)
packer.packMapSize(0); // fields
RNS::Bytes packed_payload = RNS::Bytes(packer.data(), packer.size());
// hashed part
RNS::Bytes hashed_part;
hashed_part.append(destination.hash()); // self.__destination.hash
hashed_part.append(source.hash()); // self.__source.hash
hashed_part.append(packed_payload); // msgpack.packb(self.payload)
RNS::Bytes hash = RNS::Identity::full_hash(hashed_part);
RNS::Bytes message_id = hash;
// signed part
RNS::Bytes signed_part;
signed_part.append(hashed_part); // signed_part += hashed_part
signed_part.append(hash); // self.hash
RNS::Bytes signature = source.sign(signed_part); // self.__source.sign(signed_part)
// packed
RNS::Bytes packed;
// packed.append(lxmf_destination.hash()); // self.__destination.hash (opportunistic lxmf messages dont send destination in packed data?)
packed.append(source.hash()); // self.__source.hash
packed.append(signature); // self.signature
packed.append(packed_payload); // msgpack.packb(self.payload)
size_t content_size = packed.size();
// ensure content size is not larger than allowed
if(content_size > maxEncryptedPackageMaxContentLength()){
ERROR("[LXMF] content exceeds single-packet size.");
return;
}
// send packet
RNS::Packet packet = RNS::Packet(destination, packed);
if(packet.send()){
INFO("[LXMF] message sent!");
} else {
ERROR("[LXMF] failed to send!");
}
}
void onMeshNodePacket(const RNS::Bytes& data, const RNS::Packet& packet) {
INFO("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
INFO("onMeshNodePacket: data: " + data.toHex());
INFO("onMeshNodePacket: text: \"" + data.toString() + "\"");
//TRACE("onMeshNodePacket: " + packet.debugString());
INFO("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
std::string str = data.toString();
std::string from = str.substr(0, 32);
std::string message = str.substr(32);
INFO("from: \"" + from + "\", message: " + message);
// find identity of "from"
RNS::Bytes fromHash;
fromHash.assignHex(from.c_str());
RNS::Identity destinationIdentity = RNS::Identity::recall(fromHash);
if(destinationIdentity == RNS::Type::NONE){
INFO("could not find identity for hash: \"" + from + "\"");
return;
}
// echo received messages back to sender
sendMessage(RNS::Transport::identity(), destinationIdentity, message.c_str());
}
void onLxmfDeliveryPacket(const RNS::Bytes& data, const RNS::Packet& packet) {
INFO("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
INFO("onLxmfDeliveryPacket: data: " + data.toHex());
INFO("onLxmfDeliveryPacket: text: \"" + data.toString() + "\"");
//TRACE("onLxmfDeliveryPacket: " + packet.debugString());
INFO("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
// parse sections from lxmf data
RNS::Bytes source = data.mid(0, 16);
RNS::Bytes signature = data.mid(16, 16 + 64);
RNS::Bytes packed_payload = data.mid(16 + 64);
INFO("LXMF: source: " + source.toHex());
INFO("LXMF: signature: " + signature.toHex());
INFO("LXMF: packed_payload: " + packed_payload.toHex());
// FIXME: validate signatures
// un-msgpack the payload
MsgPack::Unpacker unpacker;
unpacker.feed(packed_payload.data(), packed_payload.size()); // pass packed data to unpacker
size_t arraySize = unpacker.unpackArraySize(); // we expect to be un-msgpacking an array with 4 entries
float timestamp = unpacker.unpackFloat(); // timestamp
MsgPack::bin_t<uint8_t> titleBytes = unpacker.unpackBinary();
MsgPack::bin_t<uint8_t> contentBytes = unpacker.unpackBinary();
// size_t fieldsSize = unpacker.unpackMapSize();
// convert bytes to strings
char* title = reinterpret_cast<char*>(titleBytes.data());
char* content = reinterpret_cast<char*>(contentBytes.data());
// log
INFO("LXMF: timestamp: " + std::to_string(timestamp));
INFO(title);
INFO(content);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment