mirror of
https://github.com/markqvist/reticulum-cpp.git
synced 2024-10-01 02:55:46 -04:00
Initial commit
This commit is contained in:
parent
d3ea0eedd9
commit
eef5f1b326
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
46
lib/README
Normal file
46
lib/README
Normal file
@ -0,0 +1,46 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
46
platformio.ini
Normal file
46
platformio.ini
Normal file
@ -0,0 +1,46 @@
|
||||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
;default_envs = native
|
||||
|
||||
[env:avr-test]
|
||||
platform = atmelavr
|
||||
board = megaatmega2560
|
||||
framework = arduino
|
||||
debug_tool = simavr
|
||||
|
||||
[env:native]
|
||||
platform = native
|
||||
build_flags =
|
||||
-std=c++11
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wno-missing-field-initializers
|
||||
-Wno-format
|
||||
-Isrc
|
||||
-DNATIVE
|
||||
lib_deps =
|
||||
; operatorfoundation/Crypto@^0.4.0
|
||||
lib_compat_mode = off
|
||||
|
||||
[env:ttgo-t-beam]
|
||||
platform = espressif32
|
||||
board = ttgo-t-beam
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
build_flags =
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wno-missing-field-initializers
|
||||
-Wno-format
|
||||
-Isrc
|
||||
lib_deps =
|
||||
operatorfoundation/Crypto@^0.4.0
|
88
src/Bytes.cpp
Normal file
88
src/Bytes.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
#include "Bytes.h"
|
||||
|
||||
using namespace RNS;
|
||||
|
||||
void Bytes::ownData() {
|
||||
if (!_data) {
|
||||
newData();
|
||||
}
|
||||
else if (!_owner) {
|
||||
Data *data;
|
||||
if (!_data->empty()) {
|
||||
//extreme("Bytes is creating a writable copy of its shared data");
|
||||
data = new Data(*_data.get());
|
||||
}
|
||||
else {
|
||||
//extreme("Bytes is creating its own data because shared is empty");
|
||||
data = new Data();
|
||||
}
|
||||
_data = SharedData(data);
|
||||
_owner = true;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t Bytes::compare(const Bytes &bytes) const {
|
||||
if (_data == bytes._data) {
|
||||
return 0;
|
||||
}
|
||||
else if (!_data) {
|
||||
return -1;
|
||||
}
|
||||
else if (!bytes._data) {
|
||||
return 1;
|
||||
}
|
||||
else if (*_data < *(bytes._data)) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
char const hex_upper_chars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
char const hex_lower_chars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
|
||||
std::string Bytes::toHex(bool upper /*= true*/) const {
|
||||
if (!_data) {
|
||||
return "";
|
||||
}
|
||||
std::string hex;
|
||||
for (uint8_t byte : *_data) {
|
||||
if (upper) {
|
||||
hex += hex_upper_chars[ (byte & 0xF0) >> 4];
|
||||
hex += hex_upper_chars[ (byte & 0x0F) >> 0];
|
||||
}
|
||||
else {
|
||||
hex += hex_lower_chars[ (byte & 0xF0) >> 4];
|
||||
hex += hex_lower_chars[ (byte & 0x0F) >> 0];
|
||||
}
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
void Bytes::assignHex(const char* hex) {
|
||||
// if assignment is empty then clear data and don't bother creating new
|
||||
if (hex == nullptr || hex[0] == 0) {
|
||||
_data = nullptr;
|
||||
return;
|
||||
}
|
||||
newData();
|
||||
size_t len = strlen(hex);
|
||||
for (size_t i = 0; i < len; i += 2) {
|
||||
uint8_t byte = (hex[i] % 32 + 9) % 25 * 16 + (hex[i+1] % 32 + 9) % 25;
|
||||
_data->push_back(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void Bytes::appendHex(const char* hex) {
|
||||
// if append is empty then do nothing
|
||||
if (hex == nullptr || hex[0] == 0) {
|
||||
return;
|
||||
}
|
||||
ownData();
|
||||
size_t len = strlen(hex);
|
||||
for (size_t i = 0; i < len; i += 2) {
|
||||
uint8_t byte = (hex[i] % 32 + 9) % 25 * 16 + (hex[i+1] % 32 + 9) % 25;
|
||||
_data->push_back(byte);
|
||||
}
|
||||
}
|
199
src/Bytes.h
Normal file
199
src/Bytes.h
Normal file
@ -0,0 +1,199 @@
|
||||
#pragma once
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace RNS {
|
||||
|
||||
class Bytes {
|
||||
|
||||
private:
|
||||
//typedef std::vector<uint8_t> Data;
|
||||
using Data = std::vector<uint8_t>;
|
||||
//typedef std::shared_ptr<Data> SharedData;
|
||||
using SharedData = std::shared_ptr<Data>;
|
||||
|
||||
public:
|
||||
Bytes() {
|
||||
//extreme("Bytes object created from default, this: " + std::to_string((ulong)this) + ", data: " + std::to_string((ulong)_data.get()));
|
||||
}
|
||||
Bytes(const Bytes &bytes) {
|
||||
//extreme("Bytes is using shared data");
|
||||
_data = bytes.shareData();
|
||||
_owner = false;
|
||||
//extreme("Bytes object copy created from bytes \"" + toString() + "\", this: " + std::to_string((ulong)this) + ", data: " + std::to_string((ulong)_data.get()));
|
||||
}
|
||||
Bytes(const uint8_t *chunk, size_t size) {
|
||||
assign(chunk, size);
|
||||
//extreme(std::string("Bytes object created from chunk \"") + toString() + "\", this: " + std::to_string((ulong)this) + ", data: " + std::to_string((ulong)_data.get()));
|
||||
}
|
||||
Bytes(const char *string) {
|
||||
assign(string);
|
||||
//extreme(std::string("Bytes object created from string \"") + toString() + "\", this: " + std::to_string((ulong)this) + ", data: " + std::to_string((ulong)_data.get()));
|
||||
}
|
||||
Bytes(const std::string &string) {
|
||||
assign(string);
|
||||
//extreme(std::string("Bytes object created from std::string \"") + toString() + "\", this: " + std::to_string((ulong)this) + ", data: " + std::to_string((ulong)_data.get()));
|
||||
}
|
||||
~Bytes() {
|
||||
//extreme(std::string("Bytes object destroyed \"") + toString() + "\", this: " + std::to_string((ulong)this) + ", data: " + std::to_string((ulong)_data.get()));
|
||||
}
|
||||
|
||||
inline Bytes& operator = (const Bytes &bytes) {
|
||||
assign(bytes);
|
||||
return *this;
|
||||
}
|
||||
inline Bytes& operator += (const Bytes &bytes) {
|
||||
append(bytes);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Bytes operator + (const Bytes &bytes) const {
|
||||
Bytes newbytes(*this);
|
||||
newbytes.append(bytes);
|
||||
return newbytes;
|
||||
}
|
||||
inline bool operator == (const Bytes &bytes) const {
|
||||
return compare(bytes) == 0;
|
||||
}
|
||||
inline bool operator != (const Bytes &bytes) const {
|
||||
return compare(bytes) != 0;
|
||||
}
|
||||
inline bool operator < (const Bytes &bytes) const {
|
||||
return compare(bytes) < 0;
|
||||
}
|
||||
inline bool operator > (const Bytes &bytes) const {
|
||||
return compare(bytes) > 0;
|
||||
}
|
||||
inline operator bool() const {
|
||||
return (_data && !_data->empty());
|
||||
}
|
||||
|
||||
private:
|
||||
inline SharedData shareData() const {
|
||||
//extreme("Bytes is sharing its own data");
|
||||
_owner = false;
|
||||
return _data;
|
||||
}
|
||||
inline void newData(size_t size = 0) {
|
||||
//extreme("Bytes is creating its own data");
|
||||
if (size > 0) {
|
||||
_data = SharedData(new Data(size));
|
||||
}
|
||||
else {
|
||||
_data = SharedData(new Data());
|
||||
}
|
||||
_owner = true;
|
||||
}
|
||||
void ownData();
|
||||
|
||||
public:
|
||||
int8_t compare(const Bytes &bytes) const;
|
||||
inline size_t size() const { if (!_data) return 0; return _data->size(); }
|
||||
inline bool empty() const { if (!_data) return true; return _data->empty(); }
|
||||
inline size_t capacity() const { if (!_data) return 0; return _data->capacity(); }
|
||||
inline const uint8_t *data() const { if (!_data) return nullptr; return _data->data(); }
|
||||
|
||||
inline std::string toString() const { if (!_data) return ""; return {(const char*)data(), size()}; }
|
||||
std::string toHex(bool upper = true) const;
|
||||
|
||||
inline uint8_t *writable(size_t size) {
|
||||
newData(size);
|
||||
return _data->data();
|
||||
}
|
||||
|
||||
inline void assign(const Bytes& bytes) {
|
||||
// if assignment is empty then clear data and don't bother creating new
|
||||
if (bytes.size() <= 0) {
|
||||
_data = nullptr;
|
||||
return;
|
||||
}
|
||||
newData();
|
||||
_data->insert(_data->begin(), bytes._data->begin(), bytes._data->end());
|
||||
}
|
||||
inline void assign(const uint8_t *chunk, size_t size) {
|
||||
// if assignment is empty then clear data and don't bother creating new
|
||||
if (chunk == nullptr || size <= 0) {
|
||||
_data = nullptr;
|
||||
return;
|
||||
}
|
||||
newData();
|
||||
_data->insert(_data->begin(), chunk, chunk + size);
|
||||
}
|
||||
inline void assign(const char* string) {
|
||||
// if assignment is empty then clear data and don't bother creating new
|
||||
if (string == nullptr || string[0] == 0) {
|
||||
_data = nullptr;
|
||||
return;
|
||||
}
|
||||
newData();
|
||||
_data->insert(_data->begin(), (uint8_t *)string, (uint8_t *)string + strlen(string));
|
||||
}
|
||||
inline void assign(const std::string &string) { assign(string.c_str()); }
|
||||
void assignHex(const char* hex);
|
||||
|
||||
inline void append(const Bytes& bytes) {
|
||||
// if append is empty then do nothing
|
||||
if (bytes.size() <= 0) {
|
||||
return;
|
||||
}
|
||||
ownData();
|
||||
_data->insert(_data->end(), bytes._data->begin(), bytes._data->end());
|
||||
}
|
||||
inline void append(const uint8_t *chunk, size_t size) {
|
||||
// if append is empty then do nothing
|
||||
if (chunk == nullptr || size <= 0) {
|
||||
return;
|
||||
}
|
||||
ownData();
|
||||
_data->insert(_data->end(), chunk, chunk + size);
|
||||
}
|
||||
inline void append(const char* string) {
|
||||
// if append is empty then do nothing
|
||||
if (string == nullptr || string[0] == 0) {
|
||||
return;
|
||||
}
|
||||
ownData();
|
||||
_data->insert(_data->end(), (uint8_t *)string, (uint8_t *)string + strlen(string));
|
||||
}
|
||||
inline void append(const std::string &string) { append(string.c_str()); }
|
||||
inline void append(uint8_t byte) {
|
||||
ownData();
|
||||
_data->push_back(byte);
|
||||
}
|
||||
void appendHex(const char* hex);
|
||||
|
||||
private:
|
||||
SharedData _data;
|
||||
mutable bool _owner = true;
|
||||
|
||||
};
|
||||
|
||||
// following array function doesn't work without size since it's past as a pointer to the array sizeof() is of the pointer
|
||||
//static inline Bytes bytesFromArray(const uint8_t arr[]) { return Bytes(arr, sizeof(arr)); }
|
||||
//static inline Bytes bytesFromChunk(const uint8_t *ptr, size_t len) { return Bytes(ptr, len); }
|
||||
static inline Bytes bytesFromChunk(const uint8_t *ptr, size_t len) { return {ptr, len}; }
|
||||
//static inline Bytes bytesFromString(const char *str) { return Bytes((uint8_t*)str, strlen(str)); }
|
||||
static inline Bytes bytesFromString(const char *str) { return {(uint8_t*)str, strlen(str)}; }
|
||||
//zstatic inline Bytes bytesFromInt(const int) { return {(uint8_t*)str, strlen(str)}; }
|
||||
|
||||
static inline std::string stringFromBytes(const Bytes& bytes) { return {(const char*)bytes.data(), bytes.size()}; }
|
||||
|
||||
}
|
||||
|
||||
inline RNS::Bytes& operator << (RNS::Bytes &lhbytes, const RNS::Bytes &rhbytes) {
|
||||
lhbytes.append(rhbytes);
|
||||
return lhbytes;
|
||||
}
|
||||
|
||||
inline RNS::Bytes& operator << (RNS::Bytes &lhbytes, uint8_t rhbyte) {
|
||||
lhbytes.append(rhbyte);
|
||||
return lhbytes;
|
||||
}
|
14
src/Cryptography/Fernet.cpp
Normal file
14
src/Cryptography/Fernet.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "Fernet.h"
|
||||
|
||||
#include "../Log.h"
|
||||
|
||||
using namespace RNS::Cryptography;
|
||||
|
||||
Fernet::Fernet() {
|
||||
log("Fernet object created", LOG_EXTREME);
|
||||
}
|
||||
|
||||
Fernet::~Fernet() {
|
||||
log("Fernet object destroyed", LOG_EXTREME);
|
||||
}
|
||||
|
18
src/Cryptography/Fernet.h
Normal file
18
src/Cryptography/Fernet.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace RNS { namespace Cryptography {
|
||||
|
||||
class Fernet {
|
||||
|
||||
public:
|
||||
static const uint8_t FERNET_OVERHEAD = 48; // Bytes
|
||||
|
||||
public:
|
||||
Fernet();
|
||||
~Fernet();
|
||||
|
||||
};
|
||||
|
||||
} }
|
31
src/Cryptography/Hashes.cpp
Normal file
31
src/Cryptography/Hashes.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "Hashes.h"
|
||||
|
||||
#include "../Log.h"
|
||||
|
||||
#include <SHA256.h>
|
||||
#include <SHA512.h>
|
||||
|
||||
using namespace RNS::Cryptography;
|
||||
|
||||
/*
|
||||
The SHA primitives are abstracted here to allow platform-
|
||||
aware hardware acceleration in the future. Currently only
|
||||
uses Python's internal SHA-256 implementation. All SHA-256
|
||||
calls in RNS end up here.
|
||||
*/
|
||||
|
||||
void RNS::Cryptography::sha256(uint8_t *hash, const uint8_t *data, uint16_t data_len) {
|
||||
//extreme("Cryptography::sha256: data: " + data.toHex() );
|
||||
SHA256 digest;
|
||||
digest.reset();
|
||||
digest.update(data, data_len);
|
||||
digest.finalize(hash, 32);
|
||||
//extreme("Cryptography::sha256: hash: " + hash.toHex() );
|
||||
}
|
||||
|
||||
void RNS::Cryptography::sha512(uint8_t *hash, const uint8_t *data, uint16_t data_len) {
|
||||
SHA512 digest;
|
||||
digest.reset();
|
||||
digest.update(data, data_len);
|
||||
digest.finalize(hash, 64);
|
||||
}
|
10
src/Cryptography/Hashes.h
Normal file
10
src/Cryptography/Hashes.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace RNS { namespace Cryptography {
|
||||
|
||||
void sha256(uint8_t *hash, const uint8_t *data, uint16_t data_len);
|
||||
void sha512(uint8_t *hash, const uint8_t *data, uint16_t data_len);
|
||||
|
||||
} }
|
347
src/Destination.cpp
Normal file
347
src/Destination.cpp
Normal file
@ -0,0 +1,347 @@
|
||||
#include "Destination.h"
|
||||
|
||||
#include "Log.h"
|
||||
#include "Transport.h"
|
||||
#include "Packet.h"
|
||||
#include "Interfaces/Interface.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
using namespace RNS;
|
||||
|
||||
Destination::Destination(const Identity &identity, const directions direction, const types type, const char* app_name, const char *aspects) : _object(new Object(identity)) {
|
||||
assert(_object);
|
||||
|
||||
// Check input values and build name string
|
||||
if (strchr(app_name, '.') != nullptr) {
|
||||
throw std::invalid_argument("Dots can't be used in app names");
|
||||
}
|
||||
|
||||
_object->_type = type;
|
||||
_object->_direction = direction;
|
||||
|
||||
std::string fullaspects(aspects);
|
||||
if (!identity && direction == Destination::IN && _object->_type != Destination::PLAIN) {
|
||||
debug("Destination::Destination: identity not provided, creating new one");
|
||||
_object->_identity = Identity();
|
||||
// CBA TODO should following include a "." delimiter?
|
||||
fullaspects += _object->_identity.hexhash();
|
||||
}
|
||||
debug("Destination::Destination: full aspects: " + fullaspects);
|
||||
|
||||
if (_object->_identity && _object->_type == Destination::PLAIN) {
|
||||
throw std::invalid_argument("Selected destination type PLAIN cannot hold an identity");
|
||||
}
|
||||
|
||||
_object->_name = expand_name(_object->_identity, app_name, fullaspects.c_str());
|
||||
debug("Destination::Destination: name: " + _object->_name);
|
||||
|
||||
// Generate the destination address hash
|
||||
debug("Destination::Destination: creating hash...");
|
||||
_object->_hash = hash(_object->_identity, app_name, fullaspects.c_str());
|
||||
debug("Destination::Destination: hash: " + _object->_hash.toHex());
|
||||
// CBA TEST CRASH
|
||||
debug("Destination::Destination: creating name hash...");
|
||||
_object->_name_hash = Identity::truncated_hash(expand_name(Identity::NONE, app_name, fullaspects.c_str()));
|
||||
debug("Destination::Destination: name hash: " + _object->_name_hash.toHex());
|
||||
_object->_hexhash = _object->_hash.toHex();
|
||||
debug("Destination::Destination: hexhash: " + _object->_hexhash);
|
||||
|
||||
debug("Destination::Destination: calling register_destination");
|
||||
Transport::register_destination(*this);
|
||||
|
||||
extreme("Destination object created");
|
||||
}
|
||||
|
||||
Destination::~Destination() {
|
||||
extreme("Destination object destroyed");
|
||||
}
|
||||
|
||||
/*
|
||||
:returns: A destination name in adressable hash form, for an app_name and a number of aspects.
|
||||
*/
|
||||
/*static*/ Bytes Destination::hash(const Identity &identity, const char *app_name, const char *aspects) {
|
||||
//name_hash = Identity::full_hash(Destination.expand_name(None, app_name, *aspects).encode("utf-8"))[:(RNS.Identity.NAME_HASH_LENGTH//8)]
|
||||
//addr_hash_material = name_hash
|
||||
Bytes addr_hash_material = Identity::truncated_hash(expand_name(Identity::NONE, app_name, aspects));
|
||||
//if identity != None:
|
||||
// if isinstance(identity, RNS.Identity):
|
||||
// addr_hash_material += identity.hash
|
||||
// elif isinstance(identity, bytes) and len(identity) == RNS.Reticulum.TRUNCATED_HASHLENGTH//8:
|
||||
// addr_hash_material += identity
|
||||
// else:
|
||||
// raise TypeError("Invalid material supplied for destination hash calculation")
|
||||
addr_hash_material << identity.hash();
|
||||
|
||||
return Identity::truncated_hash(addr_hash_material);
|
||||
}
|
||||
|
||||
/*
|
||||
:returns: A string containing the full human-readable name of the destination, for an app_name and a number of aspects.
|
||||
*/
|
||||
/*static*/ std::string Destination::expand_name(const Identity &identity, const char *app_name, const char *aspects) {
|
||||
|
||||
if (strchr(app_name, '.') != nullptr) {
|
||||
throw std::invalid_argument("Dots can't be used in app names");
|
||||
}
|
||||
|
||||
std::string name(app_name);
|
||||
|
||||
if (aspects != nullptr) {
|
||||
name += std::string(".") + aspects;
|
||||
}
|
||||
|
||||
if (identity) {
|
||||
name += "." + identity.hexhash();
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/*
|
||||
Creates an announce packet for this destination and broadcasts it on all
|
||||
relevant interfaces. Application specific data can be added to the announce.
|
||||
|
||||
:param app_data: *bytes* containing the app_data.
|
||||
:param path_response: Internal flag used by :ref:`RNS.Transport<api-transport>`. Ignore.
|
||||
*/
|
||||
Packet Destination::announce(const Bytes &app_data, bool path_response, Interface *attached_interface, const Bytes &tag, bool send) {
|
||||
assert(_object);
|
||||
debug("Destination::announce: announcing destination...");
|
||||
|
||||
if (_object->_type != SINGLE) {
|
||||
throw std::invalid_argument("Only SINGLE destination types can be announced");
|
||||
}
|
||||
|
||||
if (_object->_direction != IN) {
|
||||
throw std::invalid_argument("Only IN destination types can be announced");
|
||||
}
|
||||
|
||||
time_t now = time(nullptr);
|
||||
auto it = _object->_path_responses.begin();
|
||||
while (it != _object->_path_responses.end()) {
|
||||
// vector
|
||||
//Response &entry = *it;
|
||||
// map
|
||||
Response &entry = (*it).second;
|
||||
if (now > (entry.first + Destination::PR_TAG_WINDOW)) {
|
||||
it = _object->_path_responses.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
Bytes announce_data;
|
||||
|
||||
/*
|
||||
// CBA TEST
|
||||
debug("Destination::announce: performing path test...");
|
||||
debug("Destination::announce: inserting path...");
|
||||
_object->_path_responses.insert({Bytes("foo_tag"), {0, Bytes("this is foo tag")}});
|
||||
debug("Destination::announce: inserting path...");
|
||||
_object->_path_responses.insert({Bytes("test_tag"), {0, Bytes("this is test tag")}});
|
||||
if (path_response) {
|
||||
debug("Destination::announce: path_response is true");
|
||||
}
|
||||
if (!tag.empty()) {
|
||||
debug("Destination::announce: tag is specified");
|
||||
std::string tagstr((const char*)tag.data(), tag.size());
|
||||
debug(std::string("Destination::announce: tag: ") + tagstr);
|
||||
debug(std::string("Destination::announce: tag len: ") + std::to_string(tag.size()));
|
||||
debug("Destination::announce: searching for tag...");
|
||||
if (_object->_path_responses.find(tag) != _object->_path_responses.end()) {
|
||||
debug("Destination::announce: found tag in _path_responses");
|
||||
debug(std::string("Destination::announce: data: ") +_object->_path_responses[tag].second.toString());
|
||||
}
|
||||
else {
|
||||
debug("Destination::announce: tag not found in _path_responses");
|
||||
}
|
||||
}
|
||||
debug("Destination::announce: path test finished");
|
||||
*/
|
||||
|
||||
if (path_response && !tag.empty() && _object->_path_responses.find(tag) != _object->_path_responses.end()) {
|
||||
// This code is currently not used, since Transport will block duplicate
|
||||
// path requests based on tags. When multi-path support is implemented in
|
||||
// Transport, this will allow Transport to detect redundant paths to the
|
||||
// same destination, and select the best one based on chosen criteria,
|
||||
// since it will be able to detect that a single emitted announce was
|
||||
// received via multiple paths. The difference in reception time will
|
||||
// potentially also be useful in determining characteristics of the
|
||||
// multiple available paths, and to choose the best one.
|
||||
//zextreme("Using cached announce data for answering path request with tag "+RNS.prettyhexrep(tag));
|
||||
announce_data << _object->_path_responses[tag].second;
|
||||
}
|
||||
else {
|
||||
Bytes destination_hash = _object->_hash;
|
||||
//zBytes random_hash = Identity::get_random_hash()[0:5] << int(time.time()).to_bytes(5, "big");
|
||||
Bytes random_hash;
|
||||
|
||||
Bytes new_app_data(app_data);
|
||||
if (new_app_data.empty() && !_object->_default_app_data.empty()) {
|
||||
new_app_data = _object->_default_app_data;
|
||||
}
|
||||
|
||||
Bytes signed_data;
|
||||
debug("Destination::announce: hash: " + _object->_hash.toHex());
|
||||
debug("Destination::announce: identity public key: " + _object->_identity.get_public_key().toHex());
|
||||
debug("Destination::announce: name hash: " + _object->_name_hash.toHex());
|
||||
debug("Destination::announce: random hash: " + random_hash.toHex());
|
||||
debug("Destination::announce: new app data: " + new_app_data.toHex());
|
||||
signed_data << _object->_hash << _object->_identity.get_public_key() << _object->_name_hash << random_hash;
|
||||
if (new_app_data) {
|
||||
signed_data << new_app_data;
|
||||
}
|
||||
debug("Destination::announce: signed data: " + signed_data.toHex());
|
||||
|
||||
Bytes signature(_object->_identity.sign(signed_data));
|
||||
debug("Destination::announce: signature: " + signature.toHex());
|
||||
|
||||
announce_data << _object->_identity.get_public_key() << _object->_name_hash << random_hash << signature;
|
||||
|
||||
if (new_app_data) {
|
||||
announce_data << new_app_data;
|
||||
}
|
||||
|
||||
_object->_path_responses.insert({tag, {time(nullptr), announce_data}});
|
||||
}
|
||||
debug("Destination::announce: announce_data: " + announce_data.toHex());
|
||||
|
||||
Packet::context_types announce_context = Packet::CONTEXT_NONE;
|
||||
if (path_response) {
|
||||
announce_context = Packet::PATH_RESPONSE;
|
||||
}
|
||||
|
||||
debug("Destination::announce: creating announce packet...");
|
||||
Packet announce_packet(*this, announce_data, Packet::ANNOUNCE, announce_context,Transport::BROADCAST, Packet::HEADER_1, nullptr, attached_interface);
|
||||
|
||||
if (send) {
|
||||
announce_packet.send();
|
||||
return Packet::NONE;
|
||||
}
|
||||
else {
|
||||
return announce_packet;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Registers a function to be called when a link has been established to
|
||||
this destination.
|
||||
|
||||
:param callback: A function or method with the signature *callback(link)* to be called when a new link is established with this destination.
|
||||
*/
|
||||
void Destination::set_link_established_callback(Callbacks::link_established callback) {
|
||||
assert(_object);
|
||||
_object->_callbacks._link_established = callback;
|
||||
}
|
||||
|
||||
/*
|
||||
Registers a function to be called when a packet has been received by
|
||||
this destination.
|
||||
|
||||
:param callback: A function or method with the signature *callback(data, packet)* to be called when this destination receives a packet.
|
||||
*/
|
||||
void Destination::set_packet_callback(Callbacks::packet callback) {
|
||||
assert(_object);
|
||||
_object->_callbacks._packet = callback;
|
||||
}
|
||||
|
||||
/*
|
||||
Registers a function to be called when a proof has been requested for
|
||||
a packet sent to this destination. Allows control over when and if
|
||||
proofs should be returned for received packets.
|
||||
|
||||
:param callback: A function or method to with the signature *callback(packet)* be called when a packet that requests a proof is received. The callback must return one of True or False. If the callback returns True, a proof will be sent. If it returns False, a proof will not be sent.
|
||||
*/
|
||||
void Destination::set_proof_requested_callback(Callbacks::proof_requested callback) {
|
||||
assert(_object);
|
||||
_object->_callbacks._proof_requested = callback;
|
||||
}
|
||||
|
||||
/*
|
||||
Encrypts information for ``RNS.Destination.SINGLE`` or ``RNS.Destination.GROUP`` type destination.
|
||||
|
||||
:param plaintext: A *bytes-like* containing the plaintext to be encrypted.
|
||||
:raises: ``ValueError`` if destination does not hold a necessary key for encryption.
|
||||
*/
|
||||
Bytes Destination::encrypt(const Bytes &data) {
|
||||
assert(_object);
|
||||
debug("Destination::encrypt: encrypting bytes");
|
||||
|
||||
/*
|
||||
if (_object->_type == Destination::PLAIN) {
|
||||
return data;
|
||||
}
|
||||
|
||||
if (_object->_type == Destination::SINGLE && _object->_identity) {
|
||||
return _object->_identity.encrypt(data)
|
||||
}
|
||||
|
||||
if (_object->_type == Destination::GROUP {
|
||||
if hasattr(self, "prv") and self.prv != None:
|
||||
try:
|
||||
return self.prv.encrypt(plaintext)
|
||||
except Exception as e:
|
||||
RNS.log("The GROUP destination could not encrypt data", RNS.LOG_ERROR)
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
else:
|
||||
raise ValueError("No private key held by GROUP destination. Did you create or load one?")
|
||||
}
|
||||
*/
|
||||
|
||||
// MOCK
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
Decrypts information for ``RNS.Destination.SINGLE`` or ``RNS.Destination.GROUP`` type destination.
|
||||
|
||||
:param ciphertext: *Bytes* containing the ciphertext to be decrypted.
|
||||
:raises: ``ValueError`` if destination does not hold a necessary key for decryption.
|
||||
*/
|
||||
Bytes Destination::decrypt(const Bytes &data) {
|
||||
assert(_object);
|
||||
debug("Destination::decrypt: decrypting bytes");
|
||||
|
||||
/*
|
||||
if (_object->_type == Destination::PLAIN) {
|
||||
return data;
|
||||
}
|
||||
|
||||
if (_object->_type == Destination::SINGLE && _object->_identity) {
|
||||
return identity.decrypt(data);
|
||||
}
|
||||
|
||||
if (_object->_type == Destination::GROUP) {
|
||||
if hasattr(self, "prv") and self.prv != None:
|
||||
try:
|
||||
return self.prv.decrypt(ciphertext)
|
||||
except Exception as e:
|
||||
RNS.log("The GROUP destination could not decrypt data", RNS.LOG_ERROR)
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
else:
|
||||
raise ValueError("No private key held by GROUP destination. Did you create or load one?")
|
||||
}
|
||||
*/
|
||||
|
||||
// MOCK
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
Signs information for ``RNS.Destination.SINGLE`` type destination.
|
||||
|
||||
:param message: *Bytes* containing the message to be signed.
|
||||
:returns: A *bytes-like* containing the message signature, or *None* if the destination could not sign the message.
|
||||
*/
|
||||
Bytes Destination::sign(const Bytes &message) {
|
||||
assert(_object);
|
||||
if (_object->_type == Destination::SINGLE && _object->_identity) {
|
||||
return _object->_identity.sign(message);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
166
src/Destination.h
Normal file
166
src/Destination.h
Normal file
@ -0,0 +1,166 @@
|
||||
#pragma once
|
||||
|
||||
#include "Reticulum.h"
|
||||
#include "Identity.h"
|
||||
#include "Bytes.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace RNS {
|
||||
|
||||
class Interface;
|
||||
class Packet;
|
||||
class Link;
|
||||
|
||||
/**
|
||||
* @brief A class used to describe endpoints in a Reticulum Network. Destination
|
||||
* instances are used both to create outgoing and incoming endpoints. The
|
||||
* destination type will decide if encryption, and what type, is used in
|
||||
* communication with the endpoint. A destination can also announce its
|
||||
* presence on the network, which will also distribute necessary keys for
|
||||
* encrypted communication with it.
|
||||
*
|
||||
* @param identity An instance of :ref:`RNS.Identity<api-identity>`. Can hold only public keys for an outgoing destination, or holding private keys for an ingoing.
|
||||
* @param direction ``RNS.Destination.IN`` or ``RNS.Destination.OUT``.
|
||||
* @param type ``RNS.Destination.SINGLE``, ``RNS.Destination.GROUP`` or ``RNS.Destination.PLAIN``.
|
||||
* @param app_name A string specifying the app name.
|
||||
* @param aspects Any non-zero number of string arguments.
|
||||
*/
|
||||
class Destination {
|
||||
|
||||
public:
|
||||
class Callbacks {
|
||||
public:
|
||||
using link_established = void(*)(Link *link);
|
||||
//using packet = void(*)(uint8_t *data, uint16_t data_len, Packet *packet);
|
||||
using packet = void(*)(const Bytes &data, Packet *packet);
|
||||
using proof_requested = void(*)(Packet *packet);
|
||||
|
||||
link_established _link_established = nullptr;
|
||||
packet _packet = nullptr;
|
||||
proof_requested _proof_requested = nullptr;
|
||||
};
|
||||
|
||||
//typedef std::pair<time_t, std::string> Response;
|
||||
using Response = std::pair<time_t, Bytes>;
|
||||
//using Response = std::pair<time_t, std::vector<uint8_t>>;
|
||||
|
||||
enum NoneConstructor {
|
||||
NONE
|
||||
};
|
||||
|
||||
// Constants
|
||||
enum types {
|
||||
SINGLE = 0x00,
|
||||
GROUP = 0x01,
|
||||
PLAIN = 0x02,
|
||||
LINK = 0x03,
|
||||
};
|
||||
|
||||
enum proof_strategies {
|
||||
PROVE_NONE = 0x21,
|
||||
PROVE_APP = 0x22,
|
||||
PROVE_ALL = 0x23,
|
||||
};
|
||||
|
||||
enum request_policies {
|
||||
ALLOW_NONE = 0x00,
|
||||
ALLOW_ALL = 0x01,
|
||||
ALLOW_LIST = 0x02,
|
||||
};
|
||||
|
||||
enum directions {
|
||||
IN = 0x11,
|
||||
OUT = 0x12,
|
||||
};
|
||||
|
||||
const uint8_t PR_TAG_WINDOW = 30;
|
||||
|
||||
public:
|
||||
Destination(NoneConstructor none) {
|
||||
extreme("Destination NONE object created");
|
||||
}
|
||||
Destination(const Destination &destination) : _object(destination._object) {
|
||||
extreme("Destination object copy created");
|
||||
}
|
||||
Destination(const Identity &identity, const directions direction, const types type, const char* app_name, const char *aspects);
|
||||
~Destination();
|
||||
|
||||
inline Destination& operator = (const Destination &destination) {
|
||||
_object = destination._object;
|
||||
extreme("Destination object copy created by assignment, this: " + std::to_string((ulong)this) + ", data: " + std::to_string((uint32_t)_object.get()));
|
||||
return *this;
|
||||
}
|
||||
inline operator bool() const {
|
||||
return _object.get() != nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
static Bytes hash(const Identity &identity, const char *app_name, const char *aspects);
|
||||
static std::string expand_name(const Identity &identity, const char *app_name, const char *aspects);
|
||||
|
||||
public:
|
||||
Packet announce(const Bytes &app_data = {}, bool path_response = false, Interface *attached_interface = nullptr, const Bytes &tag = {}, bool send = true);
|
||||
inline void set_accepts_links(bool accepts) { assert(_object); _object->_accept_link_requests = accepts; }
|
||||
inline bool get_accepts_links() { assert(_object); return _object->_accept_link_requests; }
|
||||
void set_link_established_callback(Callbacks::link_established callback);
|
||||
void set_packet_callback(Callbacks::packet callback);
|
||||
void set_proof_requested_callback(Callbacks::proof_requested callback);
|
||||
Bytes encrypt(const Bytes &data);
|
||||
Bytes decrypt(const Bytes &data);
|
||||
Bytes sign(const Bytes &message);
|
||||
|
||||
// getters/setters
|
||||
inline types type() const { assert(_object); return _object->_type; }
|
||||
inline directions _direction() const { assert(_object); return _object->_direction; }
|
||||
inline proof_strategies _proof_strategy() const { assert(_object); return _object->_proof_strategy; }
|
||||
inline Bytes hash() const { assert(_object); return _object->_hash; }
|
||||
inline Bytes link_id() const { assert(_object); return _object->_link_id; }
|
||||
inline uint16_t mtu() const { assert(_object); return _object->_mtu; }
|
||||
inline void mtu(uint16_t mtu) { assert(_object); _object->_mtu = mtu; }
|
||||
|
||||
private:
|
||||
class Object {
|
||||
public:
|
||||
Object(const Identity &identity) : _identity(identity) {}
|
||||
private:
|
||||
bool _accept_link_requests = true;
|
||||
Callbacks _callbacks;
|
||||
//z_request_handlers = {}
|
||||
types _type;
|
||||
directions _direction;
|
||||
proof_strategies _proof_strategy = PROVE_NONE;
|
||||
uint16_t _mtu = 0;
|
||||
|
||||
//std::vector<Response> _path_responses;
|
||||
std::map<Bytes, Response> _path_responses;
|
||||
//z_links = []
|
||||
|
||||
Identity _identity;
|
||||
std::string _name;
|
||||
|
||||
// Generate the destination address hash
|
||||
Bytes _hash;
|
||||
Bytes _name_hash;
|
||||
std::string _hexhash;
|
||||
|
||||
// CBA TODO when is _default_app_data a "callable"?
|
||||
Bytes _default_app_data;
|
||||
//z_callback = None
|
||||
//z_proofcallback = None
|
||||
|
||||
// CBA _link_id is expected by packet but only present in Link
|
||||
// CBA TODO determine if Link needs to inherit from Destination or vice-versa
|
||||
Bytes _link_id;
|
||||
friend class Destination;
|
||||
};
|
||||
std::shared_ptr<Object> _object;
|
||||
|
||||
};
|
||||
|
||||
}
|
102
src/DumbBuffer.h
Normal file
102
src/DumbBuffer.h
Normal file
@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#define COW
|
||||
|
||||
namespace RNS {
|
||||
|
||||
class Bytes {
|
||||
|
||||
public:
|
||||
Bytes(size_t capacity = 0) {
|
||||
if (capacity > 0) {
|
||||
//_vector.reserve(capacity);
|
||||
_vector = std::shared_ptr<std::vector<uint8_t>>(new std::vector<uint8_t>(capacity));
|
||||
}
|
||||
else {
|
||||
_vector = std::shared_ptr<std::vector<uint8_t>>(new std::vector<uint8_t>());
|
||||
}
|
||||
log("Bytes object created from default");
|
||||
}
|
||||
Bytes(const Bytes &bytes, size_t capacity = 0) : Bytes(capacity) {
|
||||
//append(bytes);
|
||||
_vector = bytes._vector;
|
||||
_owner = false;
|
||||
log(std::string("Bytes object created from bytes \"") + toString() + "\"");
|
||||
}
|
||||
Bytes(const uint8_t *chunk, size_t size, size_t capacity = 0) : Bytes(capacity) {
|
||||
append(chunk, size);
|
||||
log(std::string("Bytes object created from chunk \"") + toString() + "\"");
|
||||
}
|
||||
Bytes(const char *string, size_t capacity = 0) : Bytes(capacity) {
|
||||
append(string);
|
||||
log(std::string("Bytes object created from string \"") + toString() + "\"");
|
||||
}
|
||||
~Bytes() {
|
||||
log(std::string("Bytes object destroyed \"") + toString() + "\"");
|
||||
}
|
||||
|
||||
inline Bytes& operator + (const Bytes &bytes) {
|
||||
append(bytes);
|
||||
return *this;
|
||||
}
|
||||
inline Bytes& operator += (const Bytes &bytes) {
|
||||
append(bytes);
|
||||
return *this;
|
||||
}
|
||||
inline bool operator == (const Bytes &bytes) const {
|
||||
return _vector == bytes._vector;
|
||||
}
|
||||
inline bool operator < (const Bytes &bytes) const {
|
||||
return _vector < bytes._vector;
|
||||
}
|
||||
|
||||
public:
|
||||
inline size_t size() const { return _vector->size(); }
|
||||
inline bool empty() const { return _vector->empty(); }
|
||||
inline size_t capacity() const { return _vector->capacity(); }
|
||||
inline const uint8_t *data() const { return _vector->data(); }
|
||||
|
||||
void append(const Bytes& bytes) {
|
||||
_vector->insert(_vector->end(), bytes._vector->begin(), bytes._vector->end());
|
||||
}
|
||||
void append(const uint8_t *chunk, size_t size) {
|
||||
_vector->insert(_vector->end(), chunk, chunk + size);
|
||||
}
|
||||
void append(const char* string) {
|
||||
_vector->insert(_vector->end(), (uint8_t *)string, (uint8_t *)string + strlen(string));
|
||||
}
|
||||
|
||||
inline std::string toString() { return {(const char*)data(), size()}; }
|
||||
|
||||
private:
|
||||
//std::vector<uint8_t> _vector;
|
||||
std::shared_ptr<std::vector<uint8_t>> _vector;
|
||||
bool _owner = true;;
|
||||
|
||||
};
|
||||
|
||||
// following array function doesn't work without size since it's past as a pointer to the array sizeof() is of the pointer
|
||||
//static inline Bytes bytesFromArray(const uint8_t arr[]) { return Bytes(arr, sizeof(arr)); }
|
||||
//static inline Bytes bytesFromChunk(const uint8_t *ptr, size_t len) { return Bytes(ptr, len); }
|
||||
static inline Bytes bytesFromChunk(const uint8_t *ptr, size_t len) { return {ptr, len}; }
|
||||
//static inline Bytes bytesFromString(const char *str) { return Bytes((uint8_t*)str, strlen(str)); }
|
||||
static inline Bytes bytesFromString(const char *str) { return {(uint8_t*)str, strlen(str)}; }
|
||||
//zstatic inline Bytes bytesFromInt(const int) { return {(uint8_t*)str, strlen(str)}; }
|
||||
|
||||
static inline std::string stringFromBytes(const Bytes& bytes) { return {(const char*)bytes.data(), bytes.size()}; }
|
||||
|
||||
}
|
||||
|
||||
inline RNS::Bytes& operator << (RNS::Bytes &lhs, const RNS::Bytes &rhs) {
|
||||
lhs.append(rhs);
|
||||
return lhs;
|
||||
}
|
102
src/Identity.cpp
Normal file
102
src/Identity.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
#include "Identity.h"
|
||||
|
||||
#include "Log.h"
|
||||
#include "Cryptography/Hashes.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace RNS;
|
||||
|
||||
Identity::Identity(bool create_keys) : _object(new Object()) {
|
||||
if (create_keys) {
|
||||
createKeys();
|
||||
}
|
||||
extreme("Identity object created, this: " + std::to_string((ulong)this) + ", data: " + std::to_string((ulong)_object.get()));
|
||||
}
|
||||
|
||||
|
||||
void Identity::createKeys() {
|
||||
assert(_object);
|
||||
|
||||
/*
|
||||
self.prv = X25519PrivateKey.generate()
|
||||
self.prv_bytes = self.prv.private_bytes()
|
||||
|
||||
self.sig_prv = Ed25519PrivateKey.generate()
|
||||
self.sig_prv_bytes = self.sig_prv.private_bytes()
|
||||
|
||||
self.pub = self.prv.public_key()
|
||||
self.pub_bytes = self.pub.public_bytes()
|
||||
|
||||
self.sig_pub = self.sig_prv.public_key()
|
||||
self.sig_pub_bytes = self.sig_pub.public_bytes()
|
||||
*/
|
||||
|
||||
update_hashes();
|
||||
|
||||
//verbose("Identity keys created for " + _object->_hash.toHex());
|
||||
}
|
||||
|
||||
Bytes Identity::get_public_key() {
|
||||
assert(_object);
|
||||
// MOCK
|
||||
return "abc123";
|
||||
}
|
||||
|
||||
void Identity::update_hashes() {
|
||||
assert(_object);
|
||||
_object->_hash = truncated_hash(get_public_key());
|
||||
debug("Identity::update_hashes: hash: " + _object->_hash.toHex());
|
||||
_object->_hexhash = _object->_hash.toHex();
|
||||
debug("Identity::update_hashes: hexhash: " + _object->_hexhash);
|
||||
};
|
||||
|
||||
/*
|
||||
Signs information by the identity.
|
||||
|
||||
:param message: The message to be signed as *bytes*.
|
||||
:returns: Signature as *bytes*.
|
||||
:raises: *KeyError* if the instance does not hold a private key.
|
||||
*/
|
||||
Bytes Identity::sign(const Bytes &message) {
|
||||
assert(_object);
|
||||
/*
|
||||
if self.sig_prv != None:
|
||||
try:
|
||||
return self.sig_prv.sign(message)
|
||||
except Exception as e:
|
||||
RNS.log("The identity "+str(self)+" could not sign the requested message. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
raise e
|
||||
else:
|
||||
raise KeyError("Signing failed because identity does not hold a private key")
|
||||
*/
|
||||
// MOCK
|
||||
return {message};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get a SHA-256 hash of passed data.
|
||||
|
||||
:param data: Data to be hashed as *bytes*.
|
||||
:returns: SHA-256 hash as *bytes*
|
||||
*/
|
||||
/*static*/ Bytes Identity::full_hash(const Bytes &data) {
|
||||
Bytes hash;
|
||||
Cryptography::sha256(hash.writable(HASHLENGTH/8), data.data(), data.size());
|
||||
//debug("Identity::full_hash: hash: " + hash.toHex());
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
Get a truncated SHA-256 hash of passed data.
|
||||
|
||||
:param data: Data to be hashed as *bytes*.
|
||||
:returns: Truncated SHA-256 hash as *bytes*
|
||||
*/
|
||||
/*static*/ Bytes Identity::truncated_hash(const Bytes &data) {
|
||||
Bytes hash = full_hash(data);
|
||||
//Bytes truncated_hash(hash.data() + (TRUNCATED_HASHLENGTH/8), TRUNCATED_HASHLENGTH/8);
|
||||
//debug("Identity::truncated_hash: truncated hash: " + truncated_hash.toHex());
|
||||
return Bytes(hash.data() + (TRUNCATED_HASHLENGTH/8), TRUNCATED_HASHLENGTH/8);
|
||||
}
|
89
src/Identity.h
Normal file
89
src/Identity.h
Normal file
@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include "Reticulum.h"
|
||||
#include "Log.h"
|
||||
#include "Bytes.h"
|
||||
#include "Cryptography/Fernet.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace RNS {
|
||||
|
||||
class Identity {
|
||||
|
||||
public:
|
||||
enum NoneConstructor {
|
||||
NONE
|
||||
};
|
||||
|
||||
//static const char CURVE[] = "Curve25519";
|
||||
static constexpr const char* CURVE = "Curve25519";
|
||||
// The curve used for Elliptic Curve DH key exchanges
|
||||
|
||||
static const uint16_t KEYSIZE = 256*2;
|
||||
// X25519 key size in bits. A complete key is the concatenation of a 256 bit encryption key, and a 256 bit signing key.
|
||||
|
||||
// Non-configurable constants
|
||||
static const uint8_t FERNET_OVERHEAD = RNS::Cryptography::Fernet::FERNET_OVERHEAD;
|
||||
static const uint8_t AES128_BLOCKSIZE = 16; // In bytes
|
||||
static const uint16_t HASHLENGTH = Reticulum::HASHLENGTH; // In bits
|
||||
static const uint16_t SIGLENGTH = KEYSIZE; // In bits
|
||||
|
||||
static const uint8_t NAME_HASH_LENGTH = 80;
|
||||
static const uint16_t TRUNCATED_HASHLENGTH = Reticulum::TRUNCATED_HASHLENGTH; // In bits
|
||||
// Constant specifying the truncated hash length (in bits) used by Reticulum
|
||||
// for addressable hashes and other purposes. Non-configurable.
|
||||
|
||||
public:
|
||||
Identity(NoneConstructor none) {
|
||||
extreme("Identity NONE object created, this: " + std::to_string((ulong)this) + ", data: " + std::to_string((ulong)_object.get()));
|
||||
}
|
||||
Identity(const Identity &identity) : _object(identity._object) {
|
||||
extreme("Identity object copy created, this: " + std::to_string((ulong)this) + ", data: " + std::to_string((ulong)_object.get()));
|
||||
}
|
||||
Identity(bool create_keys = true);
|
||||
~Identity() {
|
||||
extreme("Identity object destroyed, this: " + std::to_string((ulong)this) + ", data: " + std::to_string((ulong)_object.get()));
|
||||
}
|
||||
|
||||
inline Identity& operator = (const Identity &identity) {
|
||||
_object = identity._object;
|
||||
extreme("Identity object copy created by assignment, this: " + std::to_string((ulong)this) + ", data: " + std::to_string((uint32_t)_object.get()));
|
||||
return *this;
|
||||
}
|
||||
inline operator bool() const {
|
||||
return _object.get() != nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
void createKeys();
|
||||
Bytes get_public_key();
|
||||
void update_hashes();
|
||||
|
||||
static Bytes full_hash(const Bytes &data);
|
||||
static Bytes truncated_hash(const Bytes &data);
|
||||
Bytes sign(const Bytes &message);
|
||||
|
||||
inline Bytes hash() const { assert(_object); return _object->_hash; }
|
||||
inline std::string hexhash() const { assert(_object); return _object->_hexhash; }
|
||||
|
||||
private:
|
||||
class Object {
|
||||
public:
|
||||
Object() {
|
||||
extreme("Identity::Data object created, this: " + std::to_string((ulong)this));
|
||||
}
|
||||
~Object() {
|
||||
extreme("Identity::Data object destroyed, this: " + std::to_string((ulong)this));
|
||||
}
|
||||
private:
|
||||
Bytes _hash;
|
||||
std::string _hexhash;
|
||||
friend class Identity;
|
||||
};
|
||||
std::shared_ptr<Object> _object;
|
||||
|
||||
};
|
||||
|
||||
}
|
14
src/Interfaces/Interface.cpp
Normal file
14
src/Interfaces/Interface.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "Interface.h"
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
using namespace RNS;
|
||||
|
||||
Interface::Interface() {
|
||||
log("Interface object created", LOG_EXTREME);
|
||||
}
|
||||
|
||||
Interface::~Interface() {
|
||||
log("Interface object destroyed", LOG_EXTREME);
|
||||
}
|
||||
|
30
src/Interfaces/Interface.h
Normal file
30
src/Interfaces/Interface.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
//#include <Arduino.h>
|
||||
|
||||
namespace RNS {
|
||||
|
||||
class Interface {
|
||||
|
||||
public:
|
||||
// Interface mode definitions
|
||||
enum modes {
|
||||
MODE_FULL = 0x01,
|
||||
MODE_POINT_TO_POINT = 0x02,
|
||||
MODE_ACCESS_POINT = 0x03,
|
||||
MODE_ROAMING = 0x04,
|
||||
MODE_BOUNDARY = 0x05,
|
||||
MODE_GATEWAY = 0x06,
|
||||
};
|
||||
|
||||
// Which interface modes a Transport Node
|
||||
// should actively discover paths for.
|
||||
//zDISCOVER_PATHS_FOR = [MODE_ACCESS_POINT, MODE_GATEWAY]
|
||||
|
||||
public:
|
||||
Interface();
|
||||
~Interface();
|
||||
|
||||
};
|
||||
|
||||
}
|
14
src/Link.cpp
Normal file
14
src/Link.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "Link.h"
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
using namespace RNS;
|
||||
|
||||
Link::Link() {
|
||||
log("Link object created", LOG_EXTREME);
|
||||
}
|
||||
|
||||
Link::~Link() {
|
||||
log("Link object destroyed", LOG_EXTREME);
|
||||
}
|
||||
|
71
src/Link.h
Normal file
71
src/Link.h
Normal file
@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include "Reticulum.h"
|
||||
#include "Identity.h"
|
||||
|
||||
namespace RNS {
|
||||
|
||||
class Link {
|
||||
|
||||
public:
|
||||
class Callbacks {
|
||||
public:
|
||||
typedef void (*established)(Link *link);
|
||||
typedef void (*closed)(Link *link);
|
||||
};
|
||||
|
||||
static constexpr const char* CURVE = Identity::CURVE;
|
||||
// The curve used for Elliptic Curve DH key exchanges
|
||||
|
||||
static const uint16_t ECPUBSIZE = 32+32;
|
||||
static const uint8_t KEYSIZE = 32;
|
||||
|
||||
//static const uint16_t MDU = floor((Reticulum::MTU-Reticulum::IFAC_MIN_SIZE-Reticulum::HEADER_MINSIZE-Identity::FERNET_OVERHEAD)/Identity::AES128_BLOCKSIZE)*Identity::AES128_BLOCKSIZE - 1;
|
||||
static const uint16_t MDU = ((Reticulum::MTU-Reticulum::IFAC_MIN_SIZE-Reticulum::HEADER_MINSIZE-Identity::FERNET_OVERHEAD)/Identity::AES128_BLOCKSIZE)*Identity::AES128_BLOCKSIZE - 1;
|
||||
|
||||
static const uint8_t ESTABLISHMENT_TIMEOUT_PER_HOP = Reticulum::DEFAULT_PER_HOP_TIMEOUT;
|
||||
// Timeout for link establishment in seconds per hop to destination.
|
||||
|
||||
static const uint16_t TRAFFIC_TIMEOUT_FACTOR = 6;
|
||||
static const uint16_t KEEPALIVE_TIMEOUT_FACTOR = 4;
|
||||
// RTT timeout factor used in link timeout calculation.
|
||||
static const uint8_t STALE_GRACE = 2;
|
||||
// Grace period in seconds used in link timeout calculation.
|
||||
static const uint16_t KEEPALIVE = 360;
|
||||
// Interval for sending keep-alive packets on established links in seconds.
|
||||
static const uint16_t STALE_TIME = 2*KEEPALIVE;
|
||||
/*
|
||||
If no traffic or keep-alive packets are received within this period, the
|
||||
link will be marked as stale, and a final keep-alive packet will be sent.
|
||||
If after this no traffic or keep-alive packets are received within ``RTT`` *
|
||||
``KEEPALIVE_TIMEOUT_FACTOR`` + ``STALE_GRACE``, the link is considered timed out,
|
||||
and will be torn down.
|
||||
*/
|
||||
|
||||
enum status {
|
||||
PENDING = 0x00,
|
||||
HANDSHAKE = 0x01,
|
||||
ACTIVE = 0x02,
|
||||
STALE = 0x03,
|
||||
CLOSED = 0x04
|
||||
};
|
||||
|
||||
enum teardown_reasons {
|
||||
TIMEOUT = 0x01,
|
||||
INITIATOR_CLOSED = 0x02,
|
||||
DESTINATION_CLOSED = 0x03,
|
||||
};
|
||||
|
||||
enum resource_strategies {
|
||||
ACCEPT_NONE = 0x00,
|
||||
ACCEPT_APP = 0x01,
|
||||
ACCEPT_ALL = 0x02,
|
||||
};
|
||||
|
||||
public:
|
||||
Link();
|
||||
~Link();
|
||||
|
||||
};
|
||||
|
||||
}
|
47
src/Log.cpp
Normal file
47
src/Log.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include "Log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace RNS;
|
||||
|
||||
const char* getLevelName(LogLevel level) {
|
||||
switch (level) {
|
||||
case LOG_CRITICAL:
|
||||
return "CRITICAL";
|
||||
case LOG_ERROR:
|
||||
return "ERROR";
|
||||
case LOG_WARNING:
|
||||
return "WARNING";
|
||||
case LOG_NOTICE:
|
||||
return "NOTICE";
|
||||
case LOG_INFO:
|
||||
return "INFO";
|
||||
case LOG_VERBOSE:
|
||||
return "VERBOSE";
|
||||
case LOG_DEBUG:
|
||||
return "DEBUG";
|
||||
case LOG_EXTREME:
|
||||
return "EXTRA";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
LogLevel _level = LOG_VERBOSE;
|
||||
|
||||
void RNS::loglevel(LogLevel level) {
|
||||
_level = level;
|
||||
}
|
||||
|
||||
void RNS::doLog(const char* msg, LogLevel level) {
|
||||
if (level > _level) {
|
||||
return;
|
||||
}
|
||||
#ifndef NATIVE
|
||||
Serial.print(getLevelName(level));
|
||||
Serial.print(" ");
|
||||
Serial.println(msg);
|
||||
#else
|
||||
printf("%s: %s\n", getLevelName(level), msg);
|
||||
#endif
|
||||
}
|
56
src/Log.h
Normal file
56
src/Log.h
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef NATIVE
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace RNS {
|
||||
|
||||
enum LogLevel {
|
||||
LOG_CRITICAL = 0,
|
||||
LOG_ERROR = 1,
|
||||
LOG_WARNING = 2,
|
||||
LOG_NOTICE = 3,
|
||||
LOG_INFO = 4,
|
||||
LOG_VERBOSE = 5,
|
||||
LOG_DEBUG = 6,
|
||||
LOG_EXTREME = 7,
|
||||
};
|
||||
|
||||
void loglevel(LogLevel level);
|
||||
|
||||
void doLog(const char* msg, LogLevel level);
|
||||
|
||||
inline void log(const char* msg, LogLevel level = LOG_NOTICE) { doLog(msg, level); }
|
||||
#ifndef NATIVE
|
||||
inline void log(const String msg, LogLevel level = LOG_NOTICE) { doLog(msg.c_str(), level); }
|
||||
#endif
|
||||
inline void log(const std::string& msg, LogLevel level = LOG_NOTICE) { doLog(msg.c_str(), level); }
|
||||
|
||||
inline void critical(const char* msg) { doLog(msg, LOG_CRITICAL); }
|
||||
inline void critical(const std::string& msg) { doLog(msg.c_str(), LOG_CRITICAL); }
|
||||
|
||||
inline void error(const char* msg) { doLog(msg, LOG_ERROR); }
|
||||
inline void error(const std::string& msg) { doLog(msg.c_str(), LOG_ERROR); }
|
||||
|
||||
inline void warning(const char* msg) { doLog(msg, LOG_WARNING); }
|
||||
inline void warning(const std::string& msg) { doLog(msg.c_str(), LOG_WARNING); }
|
||||
|
||||
inline void notice(const char* msg) { doLog(msg, LOG_NOTICE); }
|
||||
inline void notice(const std::string& msg) { doLog(msg.c_str(), LOG_NOTICE); }
|
||||
|
||||
inline void info(const char* msg) { doLog(msg, LOG_INFO); }
|
||||
inline void info(const std::string& msg) { doLog(msg.c_str(), LOG_INFO); }
|
||||
|
||||
inline void verbose(const char* msg) { doLog(msg, LOG_VERBOSE); }
|
||||
inline void verbose(const std::string& msg) { doLog(msg.c_str(), LOG_VERBOSE); }
|
||||
|
||||
inline void debug(const char* msg) { doLog(msg, LOG_DEBUG); }
|
||||
inline void debug(const std::string& msg) { doLog(msg.c_str(), LOG_DEBUG); }
|
||||
|
||||
inline void extreme(const char* msg) { doLog(msg, LOG_EXTREME); }
|
||||
inline void extreme(const std::string& msg) { doLog(msg.c_str(), LOG_EXTREME); }
|
||||
|
||||
}
|
532
src/Packet.cpp
Normal file
532
src/Packet.cpp
Normal file
@ -0,0 +1,532 @@
|
||||
#include "Packet.h"
|
||||
|
||||
#include "Identity.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace RNS;
|
||||
|
||||
Packet::Packet(const Destination &destination, const Bytes &data, types packet_type, context_types context, Transport::types transport_type, header_types header_type, const uint8_t *transport_id, Interface *attached_interface, bool create_receipt) : _object(new Object(destination)) {
|
||||
assert(_object);
|
||||
|
||||
if (_object->_destination) {
|
||||
// CBA TODO handle NONE
|
||||
if (transport_type == -1) {
|
||||
transport_type = Transport::BROADCAST;
|
||||
}
|
||||
// following moved to object constructor to avoid extra NONE object
|
||||
//_destination = destination;
|
||||
_header_type = header_type;
|
||||
_packet_type = packet_type;
|
||||
_transport_type = transport_type;
|
||||
_context = context;
|
||||
|
||||
//transport_id = transport_id;
|
||||
//setTransportId(transport_id);
|
||||
if (transport_id != nullptr) {
|
||||
memcpy(_transport_id, transport_id, Reticulum::DESTINATION_LENGTH);
|
||||
}
|
||||
|
||||
//data = data;
|
||||
//setData(data);
|
||||
if (data) {
|
||||
if (data.size() > MDU) {
|
||||
_truncated = true;
|
||||
// CBA TODO add method to truncate
|
||||
//zdata_len = MDU;
|
||||
}
|
||||
memcpy(_data, data.data(), data.size());
|
||||
}
|
||||
_flags = get_packed_flags();
|
||||
|
||||
_create_receipt = create_receipt;
|
||||
}
|
||||
else {
|
||||
//_raw = data;
|
||||
if (data) {
|
||||
memcpy(_raw, data.data(), data.size());
|
||||
}
|
||||
_packed = true;
|
||||
_fromPacked = true;
|
||||
_create_receipt = false;
|
||||
}
|
||||
_attached_interface = attached_interface;
|
||||
extreme("Packet object created");
|
||||
}
|
||||
|
||||
Packet::~Packet() {
|
||||
extreme("Packet object destroyed");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
void Packet::setTransportId(const uint8_t* transport_id) {
|
||||
if (_transport_id == nullptr) {
|
||||
delete[] _transport_id;
|
||||
_transport_id = nullptr;
|
||||
}
|
||||
if (transport_id != nullptr) {
|
||||
_transport_id = new uint8_t[Reticulum::ADDRESS_LENGTH];
|
||||
memcpy(_transport_id, transport_id, Reticulum::ADDRESS_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
void Packet::setTransportId(const uint8_t* header) {
|
||||
if (_header == nullptr) {
|
||||
delete[] _header;
|
||||
_header = nullptr;
|
||||
}
|
||||
if (header != nullptr) {
|
||||
_header = new uint8_t[HEADER_MAXSIZE];
|
||||
memcpy(_header, header, HEADER_MAXSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
void Packet::setRaw(const uint8_t* raw, uint16_t len) {
|
||||
if (_raw == nullptr) {
|
||||
delete[] _raw;
|
||||
_raw = nullptr;
|
||||
_raw_len = 0;
|
||||
}
|
||||
if (raw != nullptr) {
|
||||
_raw = new uint8_t[_MTU];
|
||||
memcpy(_raw, raw, len);
|
||||
_raw_len = len;
|
||||
}
|
||||
}
|
||||
|
||||
void Packet::setData(const uint8_t* data, uint16_t len) {
|
||||
if (_data == nullptr) {
|
||||
delete[] _data;
|
||||
_data = nullptr;
|
||||
_data_len = 0;
|
||||
}
|
||||
if (data != nullptr) {
|
||||
_data = new uint8_t[MDU];
|
||||
memcpy(_data, data, len);
|
||||
_data_len = len;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
uint8_t Packet::get_packed_flags() {
|
||||
uint8_t packed_flags = 0;
|
||||
if (_context == LRPROOF) {
|
||||
packed_flags = (_header_type << 6) | (_transport_type << 4) | (Destination::LINK << 2) | _packet_type;
|
||||
}
|
||||
else {
|
||||
packed_flags = (_header_type << 6) | (_transport_type << 4) | (_object->_destination.type() << 2) | _packet_type;
|
||||
}
|
||||
return packed_flags;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
== Reticulum Wire Format ======
|
||||
|
||||
A Reticulum packet is composed of the following fields:
|
||||
|
||||
[HEADER 2 bytes] [ADDRESSES 16/32 bytes] [CONTEXT 1 byte] [DATA 0-465 bytes]
|
||||
|
||||
* The HEADER field is 2 bytes long.
|
||||
* Byte 1: [IFAC Flag], [Header Type], [Propagation Type], [Destination Type] and [Packet Type]
|
||||
* Byte 2: Number of hops
|
||||
|
||||
* Interface Access Code field if the IFAC flag was set.
|
||||
* The length of the Interface Access Code can vary from
|
||||
1 to 64 bytes according to physical interface
|
||||
capabilities and configuration.
|
||||
|
||||
* The ADDRESSES field contains either 1 or 2 addresses.
|
||||
* Each address is 16 bytes long.
|
||||
* The Header Type flag in the HEADER field determines
|
||||
whether the ADDRESSES field contains 1 or 2 addresses.
|
||||
* Addresses are SHA-256 hashes truncated to 16 bytes.
|
||||
|
||||
* The CONTEXT field is 1 byte.
|
||||
* It is used by Reticulum to determine packet context.
|
||||
|
||||
* The DATA field is between 0 and 465 bytes.
|
||||
* It contains the packets data payload.
|
||||
|
||||
IFAC Flag
|
||||
-----------------
|
||||
open 0 Packet for publically accessible interface
|
||||
authenticated 1 Interface authentication is included in packet
|
||||
|
||||
|
||||
Header Types
|
||||
-----------------
|
||||
type 1 0 Two byte header, one 16 byte address field
|
||||
type 2 1 Two byte header, two 16 byte address fields
|
||||
|
||||
|
||||
Propagation Types
|
||||
-----------------
|
||||
broadcast 00
|
||||
transport 01
|
||||
reserved 10
|
||||
reserved 11
|
||||
|
||||
|
||||
Destination Types
|
||||
-----------------
|
||||
single 00
|
||||
group 01
|
||||
plain 10
|
||||
link 11
|
||||
|
||||
|
||||
Packet Types
|
||||
-----------------
|
||||
data 00
|
||||
announce 01
|
||||
link request 10
|
||||
proof 11
|
||||
|
||||
|
||||
+- Packet Example -+
|
||||
|
||||
HEADER FIELD DESTINATION FIELDS CONTEXT FIELD DATA FIELD
|
||||
_______|_______ ________________|________________ ________|______ __|_
|
||||
| | | | | | | |
|
||||
01010000 00000100 [HASH1, 16 bytes] [HASH2, 16 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 4
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = TRANSPORT
|
||||
|+------------- Header Type = HEADER_2 (two byte header, two address fields)
|
||||
+-------------- Access Codes = DISABLED
|
||||
|
||||
|
||||
+- Packet Example -+
|
||||
|
||||
HEADER FIELD DESTINATION FIELD CONTEXT FIELD DATA FIELD
|
||||
_______|_______ _______|_______ ________|______ __|_
|
||||
| | | | | | | |
|
||||
00000000 00000111 [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 7
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
|+------------- Header Type = HEADER_1 (two byte header, one address field)
|
||||
+-------------- Access Codes = DISABLED
|
||||
|
||||
|
||||
+- Packet Example -+
|
||||
|
||||
HEADER FIELD IFAC FIELD DESTINATION FIELD CONTEXT FIELD DATA FIELD
|
||||
_______|_______ ______|______ _______|_______ ________|______ __|_
|
||||
| | | | | | | | | |
|
||||
10000000 00000111 [IFAC, N bytes] [HASH1, 16 bytes] [CONTEXT, 1 byte] [DATA]
|
||||
|| | | | |
|
||||
|| | | | +-- Hops = 7
|
||||
|| | | +------- Packet Type = DATA
|
||||
|| | +--------- Destination Type = SINGLE
|
||||
|| +----------- Propagation Type = BROADCAST
|
||||
|+------------- Header Type = HEADER_1 (two byte header, one address field)
|
||||
+-------------- Access Codes = ENABLED
|
||||
|
||||
|
||||
Size examples of different packet types
|
||||
---------------------------------------
|
||||
|
||||
The following table lists example sizes of various
|
||||
packet types. The size listed are the complete on-
|
||||
wire size counting all fields including headers,
|
||||
but excluding any interface access codes.
|
||||
|
||||
- Path Request : 51 bytes
|
||||
- Announce : 167 bytes
|
||||
- Link Request : 83 bytes
|
||||
- Link Proof : 115 bytes
|
||||
- Link RTT packet : 99 bytes
|
||||
- Link keepalive : 20 bytes
|
||||
*/
|
||||
|
||||
|
||||
// Reticulum Packet Structure
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |I|H|PRT|DT |PT | hops | destination... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ...destination... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ...destination... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ...destination... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ...destination | context | data ... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |I|H|PRT|DT |PT | hops | destination_1... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ...destination_1... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ...destination_1... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ...destination_1... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ...destination_1 | destination_2... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ...destination_2... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ...destination_2... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ...destination_2... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ...destination_2 | context | data ... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
void Packet::pack() {
|
||||
assert(_object);
|
||||
debug("Packet::pack: packing packet...");
|
||||
|
||||
//memcpy(_destination_hash, _destination->_hash.data(), Reticulum::DESTINATION_LENGTH);
|
||||
memcpy(_destination_hash, _object->_destination.hash().data(), _object->_destination.hash().size());
|
||||
|
||||
_header[0] = _flags;
|
||||
_header[1] = _hops;
|
||||
|
||||
//uint8_t *ciphertext;
|
||||
if (_context == LRPROOF) {
|
||||
// write header
|
||||
//memcpy(_header+2, _destination->_link_id, Reticulum::DESTINATION_LENGTH);
|
||||
debug("Packet::pack: destination link id: " + _object->_destination.link_id().toHex() );
|
||||
memcpy(_header+2, _object->_destination.link_id().data(), _object->_destination.link_id().size());
|
||||
_header[Reticulum::DESTINATION_LENGTH+2] = _context;
|
||||
// prepend header to _data in _raw bytes
|
||||
memcpy(_data-Reticulum::HEADER_MINSIZE, _header, Reticulum::HEADER_MINSIZE);
|
||||
//ciphertext = _data;
|
||||
}
|
||||
else {
|
||||
if (_header_type == HEADER_1) {
|
||||
// write header
|
||||
//memcpy(_header+2, _destination->_hash.data(), Reticulum::DESTINATION_LENGTH);
|
||||
debug("Packet::pack: destination hash: " + _object->_destination.hash().toHex() );
|
||||
memcpy(_header+2, _object->_destination.hash().data(), _object->_destination.hash().size());
|
||||
_header[Reticulum::DESTINATION_LENGTH+2] = _context;
|
||||
// prepend header to _data in _raw bytes
|
||||
memcpy(_data-Reticulum::HEADER_MINSIZE, _header, Reticulum::HEADER_MINSIZE);
|
||||
|
||||
if (_packet_type == ANNOUNCE) {
|
||||
// Announce packets are not encrypted
|
||||
//ciphertext = _data;
|
||||
}
|
||||
else if (_packet_type == LINKREQUEST) {
|
||||
// Link request packets are not encrypted
|
||||
//ciphertext = _data;
|
||||
}
|
||||
else if (_packet_type == PROOF && _context == RESOURCE_PRF) {
|
||||
// Resource proofs are not encrypted
|
||||
//ciphertext = _data;
|
||||
}
|
||||
else if (_packet_type == PROOF && _object->_destination.type() == Destination::LINK) {
|
||||
// Packet proofs over links are not encrypted
|
||||
//ciphertext = _data;
|
||||
}
|
||||
else if (_context == RESOURCE) {
|
||||
// A resource takes care of encryption
|
||||
// by itself
|
||||
//ciphertext = _data;
|
||||
}
|
||||
else if (_context == KEEPALIVE) {
|
||||
// Keepalive packets contain no actual
|
||||
// data
|
||||
//ciphertext = _data;
|
||||
}
|
||||
else if (_context == CACHE_REQUEST) {
|
||||
// Cache-requests are not encrypted
|
||||
//ciphertext = _data;
|
||||
}
|
||||
else {
|
||||
// In all other cases, we encrypt the packet
|
||||
// with the destination's encryption method
|
||||
// CBA TODO Figure out how to most efficiently pass in data and receive encrypted data back into _raw bytes
|
||||
// CBA TODO Ensure that encrypted data does not exceed ENCRYPTED_MDU
|
||||
// CBA TODO Determine if encrypt method can read from and write to the same bytes
|
||||
//_data_len = _destination->encrypt(_data, _data, _data_len);
|
||||
//uint8_t data[_data_len];
|
||||
//memcpy(data, _data, _data_len);
|
||||
//_data_len = _destination->encrypt(_data, data, _data_len);
|
||||
Bytes plaintext(_data, _data_len);
|
||||
Bytes bytes = _object->_destination.encrypt(plaintext);
|
||||
_data_len = bytes.size();
|
||||
}
|
||||
}
|
||||
else if (_header_type == HEADER_2) {
|
||||
if (memcmp(_transport_id, EMPTY_DESTINATION, Reticulum::DESTINATION_LENGTH) == 0) {
|
||||
throw std::invalid_argument("Packet with header type 2 must have a transport ID");
|
||||
}
|
||||
// write header
|
||||
memcpy(_header+2, _transport_id, Reticulum::DESTINATION_LENGTH);
|
||||
//memcpy(_header+Reticulum::DESTINATION_LENGTH+2, _destination->_hash.data(), Reticulum::DESTINATION_LENGTH);
|
||||
debug("Packet::pack: destination hash: " + _object->_destination.hash().toHex() );
|
||||
memcpy(_header+Reticulum::DESTINATION_LENGTH+2, _object->_destination.hash().data(), _object->_destination.hash().size());
|
||||
_header[2*Reticulum::DESTINATION_LENGTH+2] = _context;
|
||||
// prepend header to _data in _raw bytes
|
||||
memcpy(_data-Reticulum::HEADER_MAXSIZE, _header, Reticulum::HEADER_MAXSIZE);
|
||||
|
||||
if (_packet_type == ANNOUNCE) {
|
||||
// Announce packets are not encrypted
|
||||
//ciphertext = _data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_data_len > _mtu) {
|
||||
throw std::length_error("Packet size of " + std::to_string(_data_len) + " exceeds MTU of " + std::to_string(_mtu) +" bytes");
|
||||
}
|
||||
|
||||
_packed = true;
|
||||
update_hash();
|
||||
|
||||
}
|
||||
|
||||
bool Packet::unpack() {
|
||||
assert(_object);
|
||||
debug("Packet::unpack: unpacking packet...");
|
||||
try {
|
||||
_flags = _raw[0];
|
||||
_hops = _raw[1];
|
||||
|
||||
_header_type = static_cast<header_types>((_flags & 0b01000000) >> 6);
|
||||
_transport_type = static_cast<Transport::types>((_flags & 0b00110000) >> 4);
|
||||
_destination_type = static_cast<Destination::types>((_flags & 0b00001100) >> 2);
|
||||
_packet_type = static_cast<types>(_flags & 0b00000011);
|
||||
|
||||
// CBA TODO detect invalid flags and throw error
|
||||
if (false) {
|
||||
log("Received malformed packet, dropping it.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_header_type == HEADER_2) {
|
||||
memcpy(_transport_id, _raw+2, Reticulum::DESTINATION_LENGTH);
|
||||
memcpy(_destination_hash, _raw+Reticulum::DESTINATION_LENGTH+2, Reticulum::DESTINATION_LENGTH);
|
||||
_context = static_cast<context_types>(_raw[2*Reticulum::DESTINATION_LENGTH+2]);
|
||||
_data = _raw+2*Reticulum::DESTINATION_LENGTH+3;
|
||||
}
|
||||
else {
|
||||
//memcpy(_transport_id, EMPTY_DESTINATION, Reticulum::DESTINATION_LENGTH);
|
||||
memcpy(_destination_hash, _raw+2, Reticulum::DESTINATION_LENGTH);
|
||||
_context = static_cast<context_types>(_raw[Reticulum::DESTINATION_LENGTH+2]);
|
||||
_data = _raw+Reticulum::DESTINATION_LENGTH+3;
|
||||
}
|
||||
|
||||
_packed = false;
|
||||
update_hash();
|
||||
}
|
||||
catch (std::exception& ex) {
|
||||
log(std::string("Received malformed packet, dropping it. The contained exception was: ") + ex.what(), LOG_EXTREME);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Sends the packet.
|
||||
|
||||
:returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned.
|
||||
*/
|
||||
bool Packet::send() {
|
||||
assert(_object);
|
||||
debug("Packet::send: sending packet...");
|
||||
if (_sent) {
|
||||
throw std::logic_error("Packet was already sent");
|
||||
}
|
||||
/*
|
||||
if (_destination->type == RNS::Destination::LINK) {
|
||||
if (_destination->status == RNS::Link::CLOSED) {
|
||||
throw std::runtime_error("Attempt to transmit over a closed link");
|
||||
}
|
||||
else {
|
||||
_destination->last_outbound = time();
|
||||
_destination->tx += 1;
|
||||
_destination->txbytes += _data_len;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (!_packed) {
|
||||
pack();
|
||||
}
|
||||
|
||||
if (RNS::Transport::outbound(*this)) {
|
||||
//zreturn self.receipt
|
||||
debug("Packet::send: successfully sent packet!!!");
|
||||
// MOCK
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
error("No interfaces could process the outbound packet");
|
||||
_sent = false;
|
||||
//z_receipt = None;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Re-sends the packet.
|
||||
|
||||
:returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned.
|
||||
*/
|
||||
bool Packet::resend() {
|
||||
assert(_object);
|
||||
debug("Packet::resend: re-sending packet...");
|
||||
if (!_sent) {
|
||||
throw std::logic_error("Packet was not sent yet");
|
||||
}
|
||||
// Re-pack the packet to obtain new ciphertext for
|
||||
// encrypted destinations
|
||||
pack();
|
||||
|
||||
if (RNS::Transport::outbound(*this)) {
|
||||
//zreturn self.receipt
|
||||
debug("Packet::resend: successfully sent packet!!!");
|
||||
// MOCK
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
error("No interfaces could process the outbound packet");
|
||||
_sent = false;
|
||||
//zself.receipt = None;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Packet::update_hash() {
|
||||
assert(_object);
|
||||
_packet_hash = get_hash();
|
||||
}
|
||||
|
||||
Bytes Packet::get_hash() {
|
||||
assert(_object);
|
||||
Bytes hashable_part = get_hashable_part();
|
||||
return Identity::full_hash(hashable_part);
|
||||
}
|
||||
|
||||
Bytes Packet::getTruncatedHash() {
|
||||
assert(_object);
|
||||
Bytes hashable_part = get_hashable_part();
|
||||
return Identity::truncated_hash(hashable_part);
|
||||
}
|
||||
|
||||
Bytes Packet::get_hashable_part() {
|
||||
assert(_object);
|
||||
Bytes hashable_part;
|
||||
hashable_part << (uint8_t)(_raw[0] & 0b00001111);
|
||||
hashable_part.append(_data-Reticulum::DESTINATION_LENGTH-1, _data_len+Reticulum::DESTINATION_LENGTH+1);
|
||||
return hashable_part;
|
||||
}
|
187
src/Packet.h
Normal file
187
src/Packet.h
Normal file
@ -0,0 +1,187 @@
|
||||
#pragma once
|
||||
|
||||
#include "Reticulum.h"
|
||||
#include "Identity.h"
|
||||
#include "Transport.h"
|
||||
#include "Destination.h"
|
||||
#include "Interfaces/Interface.h"
|
||||
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
namespace RNS {
|
||||
|
||||
class Packet;
|
||||
class PacketProof;
|
||||
class PacketReceipt;
|
||||
class PacketReceiptCallbacks;
|
||||
|
||||
class Packet {
|
||||
|
||||
public:
|
||||
enum NoneConstructor {
|
||||
NONE
|
||||
};
|
||||
|
||||
// Packet types
|
||||
enum types {
|
||||
DATA = 0x00, // Data packets
|
||||
ANNOUNCE = 0x01, // Announces
|
||||
LINKREQUEST = 0x02, // Link requests
|
||||
PROOF = 0x03, // Proofs
|
||||
};
|
||||
|
||||
// Header types
|
||||
enum header_types {
|
||||
HEADER_1 = 0x00, // Normal header format
|
||||
HEADER_2 = 0x01, // Header format used for packets in transport
|
||||
};
|
||||
|
||||
// Packet context types
|
||||
enum context_types {
|
||||
CONTEXT_NONE = 0x00, // Generic data packet
|
||||
RESOURCE = 0x01, // Packet is part of a resource
|
||||
RESOURCE_ADV = 0x02, // Packet is a resource advertisement
|
||||
RESOURCE_REQ = 0x03, // Packet is a resource part request
|
||||
RESOURCE_HMU = 0x04, // Packet is a resource hashmap update
|
||||
RESOURCE_PRF = 0x05, // Packet is a resource proof
|
||||
RESOURCE_ICL = 0x06, // Packet is a resource initiator cancel message
|
||||
RESOURCE_RCL = 0x07, // Packet is a resource receiver cancel message
|
||||
CACHE_REQUEST = 0x08, // Packet is a cache request
|
||||
REQUEST = 0x09, // Packet is a request
|
||||
RESPONSE = 0x0A, // Packet is a response to a request
|
||||
PATH_RESPONSE = 0x0B, // Packet is a response to a path request
|
||||
COMMAND = 0x0C, // Packet is a command
|
||||
COMMAND_STATUS = 0x0D, // Packet is a status of an executed command
|
||||
CHANNEL = 0x0E, // Packet contains link channel data
|
||||
KEEPALIVE = 0xFA, // Packet is a keepalive packet
|
||||
LINKIDENTIFY = 0xFB, // Packet is a link peer identification proof
|
||||
LINKCLOSE = 0xFC, // Packet is a link close message
|
||||
LINKPROOF = 0xFD, // Packet is a link packet proof
|
||||
LRRTT = 0xFE, // Packet is a link request round-trip time measurement
|
||||
LRPROOF = 0xFF, // Packet is a link request proof
|
||||
};
|
||||
|
||||
// This is used to calculate allowable
|
||||
// payload sizes
|
||||
static const uint16_t HEADER_MAXSIZE = Reticulum::HEADER_MAXSIZE;
|
||||
static const uint16_t MDU = Reticulum::MDU;
|
||||
|
||||
// With an MTU of 500, the maximum of data we can
|
||||
// send in a single encrypted packet is given by
|
||||
// the below calculation; 383 bytes.
|
||||
//static const uint16_t ENCRYPTED_MDU = floor((Reticulum::MDU-Identity::FERNET_OVERHEAD-Identity::KEYSIZE/16)/Identity::AES128_BLOCKSIZE)*Identity::AES128_BLOCKSIZE - 1;
|
||||
//static const uint16_t ENCRYPTED_MDU;
|
||||
static const uint16_t ENCRYPTED_MDU = ((Reticulum::MDU-Identity::FERNET_OVERHEAD-Identity::KEYSIZE/16)/Identity::AES128_BLOCKSIZE)*Identity::AES128_BLOCKSIZE - 1;
|
||||
// The maximum size of the payload data in a single encrypted packet
|
||||
static const uint16_t PLAIN_MDU = MDU;
|
||||
// The maximum size of the payload data in a single unencrypted packet
|
||||
|
||||
static const uint8_t TIMEOUT_PER_HOP = Reticulum::DEFAULT_PER_HOP_TIMEOUT;
|
||||
|
||||
//static constexpr const uint8_t EMPTY_DESTINATION[Reticulum::DESTINATION_LENGTH] = {0};
|
||||
uint8_t EMPTY_DESTINATION[Reticulum::DESTINATION_LENGTH] = {0};
|
||||
|
||||
public:
|
||||
Packet(const Destination &destination, const Bytes &data, types packet_type = DATA, context_types context = CONTEXT_NONE, Transport::types transport_type = Transport::BROADCAST, header_types header_type = HEADER_1, const uint8_t *transport_id = nullptr, Interface *attached_interface = nullptr, bool create_receipt = true);
|
||||
Packet(NoneConstructor none) {
|
||||
extreme("Packet NONE object created");
|
||||
}
|
||||
Packet(const Packet &packet) : _object(packet._object) {
|
||||
extreme("Packet object copy created");
|
||||
}
|
||||
~Packet();
|
||||
|
||||
inline Packet& operator = (const Packet &packet) {
|
||||
_object = packet._object;
|
||||
extreme("Packet object copy created by assignment, this: " + std::to_string((ulong)this) + ", data: " + std::to_string((uint32_t)_object.get()));
|
||||
return *this;
|
||||
}
|
||||
inline operator bool() const {
|
||||
return _object.get() != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
/*
|
||||
void setTransportId(const uint8_t* transport_id);
|
||||
void setHeader(const uint8_t* header);
|
||||
void setRaw(const uint8_t* raw, uint16_t len);
|
||||
void setData(const uint8_t* rata, uint16_t len);
|
||||
*/
|
||||
|
||||
private:
|
||||
class Object {
|
||||
public:
|
||||
Object(const Destination &destination) : _destination(destination) {}
|
||||
private:
|
||||
Destination _destination;
|
||||
friend class Packet;
|
||||
};
|
||||
std::shared_ptr<Object> _object;
|
||||
|
||||
public:
|
||||
uint8_t get_packed_flags();
|
||||
void pack();
|
||||
bool unpack();
|
||||
bool send();
|
||||
bool resend();
|
||||
void update_hash();
|
||||
Bytes get_hash();
|
||||
Bytes getTruncatedHash();
|
||||
Bytes get_hashable_part();
|
||||
|
||||
private:
|
||||
types _packet_type;
|
||||
header_types _header_type;
|
||||
context_types _context;
|
||||
Transport::types _transport_type;
|
||||
Destination::types _destination_type;
|
||||
|
||||
uint8_t _flags = 0;
|
||||
uint8_t _hops = 0;
|
||||
|
||||
bool _packed = false;
|
||||
bool _sent = false;
|
||||
bool _create_receipt = false;
|
||||
bool _fromPacked = false;
|
||||
bool _truncated = false;
|
||||
//z_receipt = nullptr;
|
||||
|
||||
uint16_t _mtu = Reticulum::MTU;
|
||||
time_t _sent_at = 0;
|
||||
|
||||
Interface *_attached_interface = nullptr;
|
||||
Interface *_receiving_interface = nullptr;
|
||||
|
||||
float _rssi = 0.0;
|
||||
float _snr = 0.0;
|
||||
|
||||
//uint8_t _packet_hash[Reticulum::HASHLENGTH] = {0};
|
||||
Bytes _packet_hash;
|
||||
uint8_t _destination_hash[Reticulum::DESTINATION_LENGTH] = {0};
|
||||
uint8_t _transport_id[Reticulum::DESTINATION_LENGTH] = {0};
|
||||
|
||||
uint8_t _raw[Reticulum::MTU];
|
||||
uint8_t _header[Reticulum::HEADER_MAXSIZE];
|
||||
uint8_t *_data = _raw + Reticulum::HEADER_MAXSIZE;
|
||||
uint16_t _data_len = 0;
|
||||
};
|
||||
|
||||
|
||||
class ProofDestination {
|
||||
};
|
||||
|
||||
|
||||
class PacketReceipt {
|
||||
|
||||
public:
|
||||
class Callbacks {
|
||||
public:
|
||||
typedef void (*delivery)(PacketReceipt *packet_receipt);
|
||||
typedef void (*timeout)(PacketReceipt *packet_receipt);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
14
src/Reticulum.cpp
Normal file
14
src/Reticulum.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "Reticulum.h"
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
using namespace RNS;
|
||||
|
||||
Reticulum::Reticulum() : _object(new Object()) {
|
||||
extreme("Reticulum object created");
|
||||
}
|
||||
|
||||
Reticulum::~Reticulum() {
|
||||
extreme("Reticulum object destroyed");
|
||||
}
|
||||
|
112
src/Reticulum.h
Normal file
112
src/Reticulum.h
Normal file
@ -0,0 +1,112 @@
|
||||
#pragma once
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
namespace RNS {
|
||||
|
||||
class Reticulum {
|
||||
|
||||
private:
|
||||
class Object {
|
||||
private:
|
||||
friend class Reticulum;
|
||||
};
|
||||
|
||||
public:
|
||||
enum NoneConstructor {
|
||||
NONE
|
||||
};
|
||||
|
||||
// Future minimum will probably be locked in at 251 bytes to support
|
||||
// networks with segments of different MTUs. Absolute minimum is 219.
|
||||
static const uint16_t MTU = 500;
|
||||
/*
|
||||
The MTU that Reticulum adheres to, and will expect other peers to
|
||||
adhere to. By default, the MTU is 507 bytes. In custom RNS network
|
||||
implementations, it is possible to change this value, but doing so will
|
||||
completely break compatibility with all other RNS networks. An identical
|
||||
MTU is a prerequisite for peers to communicate in the same network.
|
||||
|
||||
Unless you really know what you are doing, the MTU should be left at
|
||||
the default value.
|
||||
*/
|
||||
|
||||
static const uint16_t MAX_QUEUED_ANNOUNCES = 16384;
|
||||
static const uint32_t QUEUED_ANNOUNCE_LIFE = 60*60*24;
|
||||
|
||||
static const uint8_t ANNOUNCE_CAP = 2;
|
||||
/*
|
||||
The maximum percentage of interface bandwidth that, at any given time,
|
||||
may be used to propagate announces. If an announce was scheduled for
|
||||
broadcasting on an interface, but doing so would exceed the allowed
|
||||
bandwidth allocation, the announce will be queued for transmission
|
||||
when there is bandwidth available.
|
||||
|
||||
Reticulum will always prioritise propagating announces with fewer
|
||||
hops, ensuring that distant, large networks with many peers on fast
|
||||
links don't overwhelm the capacity of smaller networks on slower
|
||||
mediums. If an announce remains queued for an extended amount of time,
|
||||
it will eventually be dropped.
|
||||
|
||||
This value will be applied by default to all created interfaces,
|
||||
but it can be configured individually on a per-interface basis.
|
||||
*/
|
||||
|
||||
static const uint16_t MINIMUM_BITRATE = 500;
|
||||
|
||||
// TODO: To reach the 300bps level without unreasonably impacting
|
||||
// performance on faster links, we need a mechanism for setting
|
||||
// this value more intelligently. One option could be inferring it
|
||||
// from interface speed, but a better general approach would most
|
||||
// probably be to let Reticulum somehow continously build a map of
|
||||
// per-hop latencies and use this map for the timeout calculation.
|
||||
static const uint8_t DEFAULT_PER_HOP_TIMEOUT = 6;
|
||||
|
||||
static const uint16_t HASHLENGTH = 256; // In bits
|
||||
// Length of truncated hashes in bits.
|
||||
static const uint16_t TRUNCATED_HASHLENGTH = 128; // In bits
|
||||
|
||||
static const uint16_t HEADER_MINSIZE = 2+1+(TRUNCATED_HASHLENGTH/8)*1; // In bytes
|
||||
static const uint16_t HEADER_MAXSIZE = 2+1+(TRUNCATED_HASHLENGTH/8)*2; // In bytes
|
||||
static const uint16_t IFAC_MIN_SIZE = 1;
|
||||
//zIFAC_SALT = bytes.fromhex("adf54d882c9a9b80771eb4995d702d4a3e733391b2a0f53f416d9f907e55cff8")
|
||||
|
||||
static const uint16_t MDU = MTU - HEADER_MAXSIZE - IFAC_MIN_SIZE;
|
||||
|
||||
static const uint32_t RESOURCE_CACHE = 24*60*60;
|
||||
static const uint16_t JOB_INTERVAL = 5*60;
|
||||
static const uint16_t CLEAN_INTERVAL = 15*60;
|
||||
static const uint16_t PERSIST_INTERVAL = 60*60*12;
|
||||
static const uint16_t GRACIOUS_PERSIST_INTERVAL = 60*5;
|
||||
|
||||
static const uint8_t DESTINATION_LENGTH = TRUNCATED_HASHLENGTH/8; // In bytes
|
||||
|
||||
public:
|
||||
Reticulum();
|
||||
Reticulum(NoneConstructor none) {
|
||||
extreme("Reticulum NONE object created");
|
||||
}
|
||||
Reticulum(const Reticulum &reticulum) : _object(reticulum._object) {
|
||||
extreme("Reticulum object copy created");
|
||||
}
|
||||
~Reticulum();
|
||||
|
||||
inline Reticulum& operator = (const Reticulum &reticulum) {
|
||||
_object = reticulum._object;
|
||||
extreme("Reticulum object copy created by assignment, this: " + std::to_string((ulong)this) + ", data: " + std::to_string((uint32_t)_object.get()));
|
||||
return *this;
|
||||
}
|
||||
inline operator bool() const {
|
||||
return _object.get() != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Object> _object;
|
||||
|
||||
};
|
||||
|
||||
}
|
43
src/Transport.cpp
Normal file
43
src/Transport.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include "Transport.h"
|
||||
|
||||
#include "Destination.h"
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
using namespace RNS;
|
||||
|
||||
Transport::Transport() {
|
||||
log("Transport object created", LOG_EXTREME);
|
||||
}
|
||||
|
||||
Transport::~Transport() {
|
||||
log("Transport object destroyed", LOG_EXTREME);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ void Transport::register_destination(Destination &destination) {
|
||||
destination.mtu(Reticulum::MTU);
|
||||
/*
|
||||
if (destination->direction == Destination::IN) {
|
||||
for (registered_destination in Transport.destinations) {
|
||||
if (destination->hash == registered_destination->hash) {
|
||||
raise KeyError("Attempt to register an already registered destination.")
|
||||
throw std::runtime_error("Attempt to register an already registered destination.");
|
||||
}
|
||||
}
|
||||
|
||||
Transport.destinations.append(destination);
|
||||
|
||||
if (Transport.owner.is_connected_to_shared_instance) {
|
||||
if (destination->type == Destination::SINGLE) {
|
||||
destination->announce(path_response=True);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*static*/ bool Transport::outbound(const Packet &packet) {
|
||||
// mock
|
||||
return true;
|
||||
}
|
66
src/Transport.h
Normal file
66
src/Transport.h
Normal file
@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "Link.h"
|
||||
|
||||
|
||||
namespace RNS {
|
||||
|
||||
class Packet;
|
||||
class Destination;
|
||||
|
||||
class Transport {
|
||||
|
||||
public:
|
||||
// Constants
|
||||
enum types {
|
||||
BROADCAST = 0x00,
|
||||
TRANSPORT = 0x01,
|
||||
RELAY = 0x02,
|
||||
TUNNEL = 0x03,
|
||||
NONE = 0xFF,
|
||||
};
|
||||
|
||||
enum reachabilities {
|
||||
REACHABILITY_UNREACHABLE = 0x00,
|
||||
REACHABILITY_DIRECT = 0x01,
|
||||
REACHABILITY_TRANSPORT = 0x02,
|
||||
};
|
||||
|
||||
static constexpr const char* APP_NAME = "rnstransport";
|
||||
|
||||
static const uint8_t PATHFINDER_M = 128; // Max hops
|
||||
// Maximum amount of hops that Reticulum will transport a packet.
|
||||
|
||||
static const uint8_t PATHFINDER_R = 1; // Retransmit retries
|
||||
static const uint8_t PATHFINDER_G = 5; // Retry grace period
|
||||
static constexpr const float PATHFINDER_RW = 0.5; // Random window for announce rebroadcast
|
||||
static const uint32_t PATHFINDER_E = 60*60*24*7; // Path expiration of one week
|
||||
static const uint32_t AP_PATH_TIME = 60*60*24; // Path expiration of one day for Access Point paths
|
||||
static const uint32_t ROAMING_PATH_TIME = 60*60*6; // Path expiration of 6 hours for Roaming paths
|
||||
|
||||
// TODO: Calculate an optimal number for this in
|
||||
// various situations
|
||||
static const uint8_t LOCAL_REBROADCASTS_MAX = 2; // How many local rebroadcasts of an announce is allowed
|
||||
|
||||
static const uint8_t PATH_REQUEST_TIMEOUT = 15; // Default timuout for client path requests in seconds
|
||||
static constexpr const float PATH_REQUEST_GRACE = 0.35; // Grace time before a path announcement is made, allows directly reachable peers to respond first
|
||||
static const uint8_t PATH_REQUEST_RW = 2; // Path request random window
|
||||
static const uint8_t PATH_REQUEST_MI = 5; // Minimum interval in seconds for automated path requests
|
||||
|
||||
static constexpr const float LINK_TIMEOUT = Link::STALE_TIME * 1.25;
|
||||
static const uint16_t REVERSE_TIMEOUT = 30*60; // Reverse table entries are removed after 30 minutes
|
||||
static const uint32_t DESTINATION_TIMEOUT = 60*60*24*7; // Destination table entries are removed if unused for one week
|
||||
static const uint16_t MAX_RECEIPTS = 1024; // Maximum number of receipts to keep track of
|
||||
static const uint8_t MAX_RATE_TIMESTAMPS = 16; // Maximum number of announce timestamps to keep per destination
|
||||
|
||||
public:
|
||||
Transport();
|
||||
~Transport();
|
||||
|
||||
public:
|
||||
static void register_destination(Destination &destination);
|
||||
static bool outbound(const Packet &packet);
|
||||
|
||||
};
|
||||
|
||||
}
|
456
src/main.cpp
Normal file
456
src/main.cpp
Normal file
@ -0,0 +1,456 @@
|
||||
//#define NDEBUG
|
||||
|
||||
#include "Reticulum.h"
|
||||
#include "Identity.h"
|
||||
#include "Destination.h"
|
||||
#include "Packet.h"
|
||||
#include "Bytes.h"
|
||||
|
||||
#ifndef NATIVE
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
//#include <sstream>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
// Let's define an app name. We'll use this for all
|
||||
// destinations we create. Since this basic example
|
||||
// is part of a range of example utilities, we'll put
|
||||
// them all within the app namespace "example_utilities"
|
||||
const char* APP_NAME = "example_utilities";
|
||||
|
||||
// We initialise two lists of strings to use as app_data
|
||||
const char* fruits[] = {"Peach", "Quince", "Date", "Tangerine", "Pomelo", "Carambola", "Grape"};
|
||||
const char* noble_gases[] = {"Helium", "Neon", "Argon", "Krypton", "Xenon", "Radon", "Oganesson"};
|
||||
|
||||
void testMap()
|
||||
{
|
||||
const uint8_t prestr[] = "Hello";
|
||||
const uint8_t poststr[] = "World";
|
||||
|
||||
RNS::Bytes prebuf(prestr, 5);
|
||||
assert(prebuf.size() == 5);
|
||||
assert(memcmp(prebuf.data(), "Hello", prebuf.size()) == 0);
|
||||
|
||||
RNS::Bytes postbuf(poststr, 5);
|
||||
assert(postbuf.size() == 5);
|
||||
assert(memcmp(postbuf.data(), "World", postbuf.size()) == 0);
|
||||
|
||||
std::map<RNS::Bytes, std::string> map;
|
||||
map.insert({prebuf, "hello"});
|
||||
map.insert({postbuf, "world"});
|
||||
assert(map.size() == 2);
|
||||
|
||||
auto preit = map.find(prebuf);
|
||||
assert(preit != map.end());
|
||||
assert((*preit).second.compare("hello") == 0);
|
||||
//if (preit != map.end()) {
|
||||
// RNS::log(std::string("found prebuf: ") + (*preit).second);
|
||||
//}
|
||||
|
||||
auto postit = map.find(postbuf);
|
||||
assert(postit != map.end());
|
||||
assert((*postit).second.compare("world") == 0);
|
||||
//if (postit != map.end()) {
|
||||
// RNS::log(std::string("found postbuf: ") + (*postit).second);
|
||||
//}
|
||||
|
||||
const uint8_t newstr[] = "World";
|
||||
RNS::Bytes newbuf(newstr, 5);
|
||||
assert(newbuf.size() == 5);
|
||||
assert(memcmp(newbuf.data(), "World", newbuf.size()) == 0);
|
||||
auto newit = map.find(newbuf);
|
||||
assert(newit != map.end());
|
||||
assert((*newit).second.compare("world") == 0);
|
||||
//if (newit != map.end()) {
|
||||
// RNS::log(std::string("found newbuf: ") + (*newit).second);
|
||||
//}
|
||||
|
||||
std::string str = map["World"];
|
||||
assert(str.size() == 5);
|
||||
assert(str.compare("world") == 0);
|
||||
}
|
||||
|
||||
void testBytes() {
|
||||
|
||||
RNS::Bytes bytes;
|
||||
assert(!bytes);
|
||||
assert(bytes.size() == 0);
|
||||
assert(bytes.empty() == true);
|
||||
assert(bytes.data() == nullptr);
|
||||
|
||||
const uint8_t prestr[] = "Hello";
|
||||
const uint8_t poststr[] = " World";
|
||||
|
||||
RNS::Bytes prebuf(prestr, 5);
|
||||
assert(prebuf);
|
||||
assert(prebuf.size() == 5);
|
||||
assert(memcmp(prebuf.data(), "Hello", prebuf.size()) == 0);
|
||||
assert(!(bytes == prebuf));
|
||||
assert(bytes != prebuf);
|
||||
assert(bytes < prebuf);
|
||||
|
||||
RNS::Bytes postbuf(poststr, 6);
|
||||
assert(postbuf);
|
||||
assert(postbuf.size() == 6);
|
||||
assert(memcmp(postbuf.data(), " World", postbuf.size()) == 0);
|
||||
assert(!(postbuf == bytes));
|
||||
assert(postbuf != bytes);
|
||||
assert(postbuf > bytes);
|
||||
|
||||
assert(!(prebuf == postbuf));
|
||||
assert(prebuf != postbuf);
|
||||
|
||||
//if (prebuf == postbuf) {
|
||||
// RNS::log("bytess are the same");
|
||||
//}
|
||||
//else {
|
||||
// RNS::log("bytess are different");
|
||||
//}
|
||||
|
||||
bytes += prebuf + postbuf;
|
||||
assert(bytes.size() == 11);
|
||||
assert(memcmp(bytes.data(), "Hello World", bytes.size()) == 0);
|
||||
//RNS::log("assign bytes: " + bytes.toString());
|
||||
//RNS::log("assign prebuf: " + prebuf.toString());
|
||||
//RNS::log("assign postbuf: " + postbuf.toString());
|
||||
|
||||
bytes = "Foo";
|
||||
assert(bytes.size() == 3);
|
||||
assert(memcmp(bytes.data(), "Foo", bytes.size()) == 0);
|
||||
|
||||
bytes = prebuf + postbuf;
|
||||
assert(bytes.size() == 11);
|
||||
assert(memcmp(bytes.data(), "Hello World", bytes.size()) == 0);
|
||||
|
||||
// stream into empty bytes
|
||||
{
|
||||
RNS::Bytes strmbuf;
|
||||
strmbuf << prebuf << postbuf;
|
||||
//RNS::extreme("stream strmbuf: " + strmbuf.toString());
|
||||
//RNS::extreme("stream prebuf: " + prebuf.toString());
|
||||
//RNS::extreme("stream postbuf: " + postbuf.toString());
|
||||
assert(strmbuf.size() == 11);
|
||||
assert(memcmp(strmbuf.data(), "Hello World", strmbuf.size()) == 0);
|
||||
assert(prebuf.size() == 5);
|
||||
assert(memcmp(prebuf.data(), "Hello", prebuf.size()) == 0);
|
||||
assert(postbuf.size() == 6);
|
||||
assert(memcmp(postbuf.data(), " World", postbuf.size()) == 0);
|
||||
}
|
||||
|
||||
// stream into populated bytes
|
||||
{
|
||||
RNS::Bytes strmbuf("Stream ");
|
||||
assert(strmbuf);
|
||||
assert(strmbuf.size() == 7);
|
||||
assert(memcmp(strmbuf.data(), "Stream ", strmbuf.size()) == 0);
|
||||
|
||||
strmbuf << prebuf << postbuf;
|
||||
//RNS::extreme("stream strmbuf: " + strmbuf.toString());
|
||||
//RNS::extreme("stream prebuf: " + prebuf.toString());
|
||||
//RNS::extreme("stream postbuf: " + postbuf.toString());
|
||||
assert(strmbuf.size() == 18);
|
||||
assert(memcmp(strmbuf.data(), "Stream Hello World", strmbuf.size()) == 0);
|
||||
assert(prebuf.size() == 5);
|
||||
assert(memcmp(prebuf.data(), "Hello", prebuf.size()) == 0);
|
||||
assert(postbuf.size() == 6);
|
||||
assert(memcmp(postbuf.data(), " World", postbuf.size()) == 0);
|
||||
}
|
||||
|
||||
// stream with assignment
|
||||
// (this is a known and correct but perhaps unexpected and non-intuitive side-effect of assignment with stream)
|
||||
{
|
||||
RNS::Bytes strmbuf = prebuf << postbuf;
|
||||
//RNS::extreme("stream strmbuf: " + strmbuf.toString());
|
||||
//RNS::extreme("stream prebuf: " + prebuf.toString());
|
||||
//RNS::extreme("stream postbuf: " + postbuf.toString());
|
||||
assert(strmbuf.size() == 11);
|
||||
assert(memcmp(strmbuf.data(), "Hello World", strmbuf.size()) == 0);
|
||||
assert(prebuf.size() == 11);
|
||||
assert(memcmp(prebuf.data(), "Hello World", prebuf.size()) == 0);
|
||||
assert(postbuf.size() == 6);
|
||||
assert(memcmp(postbuf.data(), " World", postbuf.size()) == 0);
|
||||
}
|
||||
|
||||
{
|
||||
RNS::Bytes nonebuf = nullptr;
|
||||
assert(!nonebuf);
|
||||
assert(nonebuf.size() == 0);
|
||||
assert(nonebuf.data() == nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void testCowBytes() {
|
||||
|
||||
RNS::Bytes bytes1("1");
|
||||
assert(bytes1.size() == 1);
|
||||
assert(memcmp(bytes1.data(), "1", bytes1.size()) == 0);
|
||||
|
||||
RNS::Bytes bytes2(bytes1);
|
||||
assert(bytes2.size() == 1);
|
||||
assert(memcmp(bytes2.data(), "1", bytes2.size()) == 0);
|
||||
assert(bytes2.data() == bytes1.data());
|
||||
|
||||
RNS::Bytes bytes3(bytes2);
|
||||
assert(bytes3.size() == 1);
|
||||
assert(memcmp(bytes3.data(), "1", bytes3.size()) == 0);
|
||||
assert(bytes3.data() == bytes2.data());
|
||||
|
||||
//RNS::log("pre bytes1 ptr: " + std::to_string((uint32_t)bytes1.data()) + " data: " + bytes1.toString());
|
||||
//RNS::log("pre bytes2 ptr: " + std::to_string((uint32_t)bytes2.data()) + " data: " + bytes2.toString());
|
||||
//RNS::log("pre bytes3 ptr: " + std::to_string((uint32_t)bytes3.data()) + " data: " + bytes3.toString());
|
||||
|
||||
//bytes1.append("mississippi");
|
||||
//assert(bytes1.size() == 12);
|
||||
//assert(memcmp(bytes1.data(), "1mississippi", bytes1.size()) == 0);
|
||||
//assert(bytes1.data() != bytes2.data());
|
||||
|
||||
bytes2.append("mississippi");
|
||||
assert(bytes2.size() == 12);
|
||||
assert(memcmp(bytes2.data(), "1mississippi", bytes2.size()) == 0);
|
||||
assert(bytes2.data() != bytes1.data());
|
||||
|
||||
bytes3.assign("mississippi");
|
||||
assert(bytes3.size() == 11);
|
||||
assert(memcmp(bytes3.data(), "mississippi", bytes3.size()) == 0);
|
||||
assert(bytes3.data() != bytes2.data());
|
||||
|
||||
//RNS::log("post bytes1 ptr: " + std::to_string((uint32_t)bytes1.data()) + " data: " + bytes1.toString());
|
||||
//RNS::log("post bytes2 ptr: " + std::to_string((uint32_t)bytes2.data()) + " data: " + bytes2.toString());
|
||||
//RNS::log("post bytes3 ptr: " + std::to_string((uint32_t)bytes3.data()) + " data: " + bytes3.toString());
|
||||
}
|
||||
|
||||
void testObjects() {
|
||||
|
||||
RNS::Reticulum reticulum_default;
|
||||
assert(reticulum_default);
|
||||
|
||||
RNS::Reticulum reticulum_none(RNS::Reticulum::NONE);
|
||||
assert(!reticulum_none);
|
||||
|
||||
RNS::Reticulum reticulum_default_copy(reticulum_default);
|
||||
assert(reticulum_default_copy);
|
||||
|
||||
RNS::Reticulum reticulum_none_copy(reticulum_none);
|
||||
assert(!reticulum_none_copy);
|
||||
|
||||
}
|
||||
|
||||
void testBytesConversion() {
|
||||
|
||||
{
|
||||
RNS::Bytes bytes("Hello World");
|
||||
std::string hex = bytes.toHex();
|
||||
RNS::extreme("text: \"" + bytes.toString() + "\" upper hex: \"" + hex + "\"");
|
||||
assert(hex.length() == 22);
|
||||
assert(hex.compare("48656C6C6F20576F726C64") == 0);
|
||||
}
|
||||
{
|
||||
RNS::Bytes bytes("Hello World");
|
||||
std::string hex = bytes.toHex(false);
|
||||
RNS::extreme("text: \"" + bytes.toString() + "\" lower hex: \"" + hex + "\"");
|
||||
assert(hex.length() == 22);
|
||||
assert(hex.compare("48656c6c6f20576f726c64") == 0);
|
||||
}
|
||||
{
|
||||
std::string hex("48656C6C6F20576F726C64");
|
||||
RNS::Bytes bytes;
|
||||
bytes.assignHex(hex.c_str());
|
||||
std::string text = bytes.toString();
|
||||
RNS::extreme("hex: \"" + hex + "\" text: \"" + text + "\"");
|
||||
assert(text.length() == 11);
|
||||
assert(text.compare("Hello World") == 0);
|
||||
}
|
||||
{
|
||||
std::string hex("48656c6c6f20576f726c64");
|
||||
RNS::Bytes bytes;
|
||||
bytes.assignHex(hex.c_str());
|
||||
std::string text = bytes.toString();
|
||||
RNS::extreme("hex: \"" + hex + "\" text: \"" + text + "\"");
|
||||
assert(text.length() == 11);
|
||||
assert(text.compare("Hello World") == 0);
|
||||
|
||||
bytes.assignHex(hex.c_str());
|
||||
text = bytes.toString();
|
||||
RNS::extreme("hex: \"" + hex + "\" text: \"" + text + "\"");
|
||||
assert(text.length() == 11);
|
||||
assert(text.compare("Hello World") == 0);
|
||||
|
||||
bytes.appendHex(hex.c_str());
|
||||
text = bytes.toString();
|
||||
RNS::extreme("hex: \"" + hex + "\" text: \"" + text + "\"");
|
||||
assert(text.length() == 22);
|
||||
assert(text.compare("Hello WorldHello World") == 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
void announceLoop(RNS::Destination &destination_1, RNS::Destination &destination_2) {
|
||||
// Let the user know that everything is ready
|
||||
RNS::log("Announce example running, hit enter to manually send an announce (Ctrl-C to quit)");
|
||||
|
||||
// We enter a loop that runs until the users exits.
|
||||
// If the user hits enter, we will announce our server
|
||||
// destination on the network, which will let clients
|
||||
// know how to create messages directed towards it.
|
||||
//while (true) {
|
||||
//zentered = input();
|
||||
RNS::log("Sending announce...");
|
||||
|
||||
// Randomly select a fruit
|
||||
//const char* fruit = fruits[rand() % sizeof(fruits)];
|
||||
const char* fruit = fruits[rand() % 7];
|
||||
//RNS::log(fruit);
|
||||
RNS::log(std::string("fruit: ") + fruit);
|
||||
|
||||
// Send the announce including the app data
|
||||
if (destination_1) {
|
||||
//destination_1.announce(RNS::bytesFromString(fruit));
|
||||
// CBA TEST path
|
||||
destination_1.announce(RNS::bytesFromString(fruit), true, nullptr, RNS::bytesFromString("test_tag"));
|
||||
//zRNS::log(std::string("Sent announce from ") + RNS::prettyhexrep(destination_1->_hash) +" ("+ (const char*)destination_1->_name + ")");
|
||||
}
|
||||
|
||||
// Randomly select a noble gas
|
||||
//const char* noble_gas = noble_gases[rand() % sizeof(noble_gas)];
|
||||
const char* noble_gas = noble_gases[rand() % 7];
|
||||
//RNS::log(noble_gas);
|
||||
RNS::log(std::string("noble_gas: ") + noble_gas);
|
||||
|
||||
// Send the announce including the app data
|
||||
if (destination_2) {
|
||||
destination_2.announce(RNS::bytesFromString(noble_gas));
|
||||
//zRNS::log(std::string("Sent announce from ") + RNS::prettyhexrep(destination_2->_hash) + " (" + destination_2->_name + ")");
|
||||
}
|
||||
|
||||
#ifndef NATIVE
|
||||
delay(1000);
|
||||
#else
|
||||
usleep(1000000);
|
||||
#endif
|
||||
//}
|
||||
}
|
||||
|
||||
// This initialisation is executed when the program is started
|
||||
void program_setup() {
|
||||
|
||||
// We must first initialise Reticulum
|
||||
RNS::Reticulum reticulum;
|
||||
|
||||
// Randomly create a new identity for our example
|
||||
RNS::Identity identity;
|
||||
|
||||
// Using the identity we just created, we create two destinations
|
||||
// in the "example_utilities.announcesample" application space.
|
||||
//
|
||||
// Destinations are endpoints in Reticulum, that can be addressed
|
||||
// and communicated with. Destinations can also announce their
|
||||
// existence, which will let the network know they are reachable
|
||||
// and autoomatically create paths to them, from anywhere else
|
||||
// in the network.
|
||||
RNS::Destination destination_1(identity, RNS::Destination::IN, RNS::Destination::SINGLE, APP_NAME, "announcesample.fruits");
|
||||
// CBA TEST no identity
|
||||
//RNS::Destination destination_1(RNS::Identity::NONE, RNS::Destination::IN, RNS::Destination::SINGLE, APP_NAME, "announcesample.fruits");
|
||||
|
||||
//RNS::Destination *destination_2(identity, RNS::Destination::IN, RNS::Destination::SINGLE, APP_NAME, "announcesample.noble_gases");
|
||||
RNS::Destination destination_2(RNS::Destination::NONE);
|
||||
|
||||
// We configure the destinations to automatically prove all
|
||||
// packets adressed to it. By doing this, RNS will automatically
|
||||
// generate a proof for each incoming packet and transmit it
|
||||
// back to the sender of that packet. This will let anyone that
|
||||
// tries to communicate with the destination know whether their
|
||||
// communication was received correctly.
|
||||
//zdestination_1->set_proof_strategy(RNS::Destination::PROVE_ALL);
|
||||
//zdestination_2->set_proof_strategy(RNS::Destination::PROVE_ALL);
|
||||
|
||||
// We create an announce handler and configure it to only ask for
|
||||
// announces from "example_utilities.announcesample.fruits".
|
||||
// Try changing the filter and see what happens.
|
||||
//zannounce_handler = ExampleAnnounceHandler(
|
||||
//z aspect_filter="example_utilities.announcesample.fruits";
|
||||
//z)
|
||||
|
||||
// We register the announce handler with Reticulum
|
||||
//zRNS::Transport.register_announce_handler(announce_handler);
|
||||
|
||||
// Everything's ready!
|
||||
// Let's hand over control to the announce loop
|
||||
announceLoop(destination_1, destination_2);
|
||||
}
|
||||
*/
|
||||
|
||||
#ifndef NATIVE
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.print("Hello from T-Beam on PlatformIO!\n");
|
||||
|
||||
//std::stringstream test;
|
||||
// !!! just adding this single stringstream alone (not even using it) adds a whopping 17.1% !!!
|
||||
// !!! JUST SAY NO TO STRINGSTREAM !!!
|
||||
|
||||
RNS::loglevel(RNS::LOG_EXTREME);
|
||||
|
||||
// 18.5% completely empty program
|
||||
|
||||
// 21.8% baseline here with serial
|
||||
|
||||
RNS::Reticulum reticulum;
|
||||
// 21.9% (+0.1%)
|
||||
|
||||
RNS::Identity identity;
|
||||
// 22.6% (+0.7%)
|
||||
|
||||
RNS::Destination destination(identity, RNS::Destination::IN, RNS::Destination::SINGLE, "test", "context");
|
||||
// 23.0% (+0.4%)
|
||||
|
||||
destination.announce(RNS::bytesFromString("fruit"), true, nullptr, RNS::bytesFromString("test_tag"));
|
||||
// 23.9% (+0.8%)
|
||||
|
||||
#ifndef NDEBUG
|
||||
// begin with logging turned down for unit tests
|
||||
RNS::loglevel(RNS::LOG_WARNING);
|
||||
//RNS::loglevel(RNS::LOG_EXTREME);
|
||||
|
||||
testMap();
|
||||
testBytes();
|
||||
testCowBytes();
|
||||
testObjects();
|
||||
testBytesConversion();
|
||||
|
||||
// 24.8% (+0.9%)
|
||||
#endif
|
||||
|
||||
// increase logging for functional tests
|
||||
RNS::loglevel(RNS::LOG_EXTREME);
|
||||
|
||||
//program_setup();
|
||||
|
||||
Serial.print("Goodbye from T-Beam on PlatformIO!\n");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
printf("Hello from Native on PlatformIO!\n");
|
||||
|
||||
program_setup();
|
||||
|
||||
printf("Goodbye from Native on PlatformIO!\n");
|
||||
}
|
||||
|
||||
#endif
|
11
test/README
Normal file
11
test/README
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PlatformIO Test Runner and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
157
test/test.cpp
Normal file
157
test/test.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
#include "Reticulum.h"
|
||||
#include "Identity.h"
|
||||
#include "Destination.h"
|
||||
|
||||
#ifndef NATIVE
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <unity.h>
|
||||
|
||||
// Let's define an app name. We'll use this for all
|
||||
// destinations we create. Since this basic example
|
||||
// is part of a range of example utilities, we'll put
|
||||
// them all within the app namespace "example_utilities"
|
||||
const char* APP_NAME = "example_utilities";
|
||||
|
||||
// We initialise two lists of strings to use as app_data
|
||||
const char* fruits[] = {"Peach", "Quince", "Date", "Tangerine", "Pomelo", "Carambola", "Grape"};
|
||||
const char* noble_gases[] = {"Helium", "Neon", "Argon", "Krypton", "Xenon", "Radon", "Oganesson"};
|
||||
|
||||
void announceLoop(RNS::Destination* destination_1, RNS::Destination* destination_2) {
|
||||
// Let the user know that everything is ready
|
||||
RNS::log("Announce example running, hit enter to manually send an announce (Ctrl-C to quit)");
|
||||
|
||||
// We enter a loop that runs until the users exits.
|
||||
// If the user hits enter, we will announce our server
|
||||
// destination on the network, which will let clients
|
||||
// know how to create messages directed towards it.
|
||||
while (true) {
|
||||
//zentered = input();
|
||||
#ifndef NATIVE
|
||||
delay(1000);
|
||||
#else
|
||||
usleep(1000);
|
||||
#endif
|
||||
RNS::log("Sending announce...");
|
||||
|
||||
// Randomly select a fruit
|
||||
//const char* fruit = fruits[rand() % sizeof(fruits)];
|
||||
const char* fruit = fruits[rand() % 7];
|
||||
RNS::log(fruit);
|
||||
//RNS::log(String("fruit: ") + fruit);
|
||||
|
||||
// Send the announce including the app data
|
||||
/*
|
||||
destination_1->announce(app_data=fruit.encode("utf-8"));
|
||||
RNS::log(
|
||||
"Sent announce from "+
|
||||
RNS.prettyhexrep(destination_1.hash)+
|
||||
" ("+destination_1.name+")"
|
||||
);
|
||||
*/
|
||||
|
||||
// Randomly select a noble gas
|
||||
//const char* noble_gas = noble_gases[rand() % sizeof(noble_gas)];
|
||||
const char* noble_gas = noble_gases[rand() % 7];
|
||||
RNS::log(noble_gas);
|
||||
//RNS::log(String("noble_gas: ") + noble_gas);
|
||||
|
||||
// Send the announce including the app data
|
||||
/*
|
||||
destination_2->announce(app_data=noble_gas.encode("utf-8"));
|
||||
RNS::log(
|
||||
"Sent announce from "+
|
||||
RNS.prettyhexrep(destination_2.hash)+
|
||||
" ("+destination_2.name+")"
|
||||
);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// This initialisation is executed when the program is started
|
||||
void program_setup() {
|
||||
// We must first initialise Reticulum
|
||||
RNS::Reticulum* reticulum = new RNS::Reticulum();
|
||||
|
||||
// Randomly create a new identity for our example
|
||||
RNS::Identity* identity = new RNS::Identity();
|
||||
|
||||
// Using the identity we just created, we create two destinations
|
||||
// in the "example_utilities.announcesample" application space.
|
||||
//
|
||||
// Destinations are endpoints in Reticulum, that can be addressed
|
||||
// and communicated with. Destinations can also announce their
|
||||
// existence, which will let the network know they are reachable
|
||||
// and autoomatically create paths to them, from anywhere else
|
||||
// in the network.
|
||||
RNS::Destination* destination_1 = new RNS::Destination(
|
||||
identity,
|
||||
RNS::Destination::IN,
|
||||
RNS::Destination::SINGLE,
|
||||
APP_NAME,
|
||||
//z"announcesample",
|
||||
//z"fruits"
|
||||
nullptr
|
||||
);
|
||||
|
||||
RNS::Destination* destination_2 = new RNS::Destination(
|
||||
identity,
|
||||
RNS::Destination::IN,
|
||||
RNS::Destination::SINGLE,
|
||||
APP_NAME,
|
||||
//z"announcesample",
|
||||
//z"noble_gases"
|
||||
nullptr
|
||||
);
|
||||
|
||||
// We configure the destinations to automatically prove all
|
||||
// packets adressed to it. By doing this, RNS will automatically
|
||||
// generate a proof for each incoming packet and transmit it
|
||||
// back to the sender of that packet. This will let anyone that
|
||||
// tries to communicate with the destination know whether their
|
||||
// communication was received correctly.
|
||||
//zdestination_1->set_proof_strategy(RNS::Destination::PROVE_ALL);
|
||||
//zdestination_2->set_proof_strategy(RNS::Destination::PROVE_ALL);
|
||||
|
||||
// We create an announce handler and configure it to only ask for
|
||||
// announces from "example_utilities.announcesample.fruits".
|
||||
// Try changing the filter and see what happens.
|
||||
//zannounce_handler = ExampleAnnounceHandler(
|
||||
//z aspect_filter="example_utilities.announcesample.fruits";
|
||||
//z)
|
||||
|
||||
// We register the announce handler with Reticulum
|
||||
//zRNS::Transport.register_announce_handler(announce_handler);
|
||||
|
||||
// Everything's ready!
|
||||
// Let's hand over control to the announce loop
|
||||
announceLoop(destination_1, destination_2);
|
||||
}
|
||||
|
||||
|
||||
#ifndef NATIVE
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
RNS::log("Hello T-Beam from PlatformIO!");
|
||||
|
||||
program_setup();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
RNS::log("Hello Native from PlatformIO!");
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(program_setup);
|
||||
UNITY_END();
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user