mirror of
https://github.com/markqvist/reticulum-cpp.git
synced 2024-10-01 02:55:46 -04:00
WIP update
Implemented X25519.
This commit is contained in:
parent
1b919a9489
commit
4b062aa485
@ -39,27 +39,6 @@ int8_t Bytes::compare(const Bytes &bytes) const {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -86,3 +65,34 @@ void Bytes::appendHex(const char* hex) {
|
||||
_data->push_back(byte);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Bytes Bytes::mid(size_t pos, size_t len) const {
|
||||
if (!_data || pos >= size()) {
|
||||
return NONE;
|
||||
}
|
||||
if ((pos + len) >= size()) {
|
||||
len = (size() - pos);
|
||||
}
|
||||
return {data() + pos, len};
|
||||
}
|
||||
|
42
src/Bytes.h
42
src/Bytes.h
@ -12,6 +12,8 @@
|
||||
|
||||
namespace RNS {
|
||||
|
||||
#define COW
|
||||
|
||||
class Bytes {
|
||||
|
||||
private:
|
||||
@ -34,8 +36,7 @@ namespace RNS {
|
||||
}
|
||||
Bytes(const Bytes &bytes) {
|
||||
//extreme("Bytes is using shared data");
|
||||
_data = bytes.shareData();
|
||||
_owner = false;
|
||||
assign(bytes);
|
||||
//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) {
|
||||
@ -103,21 +104,11 @@ namespace RNS {
|
||||
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) {
|
||||
#ifdef COW
|
||||
_data = bytes.shareData();
|
||||
_owner = false;
|
||||
#else
|
||||
// if assignment is empty then clear data and don't bother creating new
|
||||
if (bytes.size() <= 0) {
|
||||
_data = nullptr;
|
||||
@ -125,6 +116,7 @@ namespace RNS {
|
||||
}
|
||||
newData();
|
||||
_data->insert(_data->begin(), bytes._data->begin(), bytes._data->end());
|
||||
#endif
|
||||
}
|
||||
inline void assign(const uint8_t *chunk, size_t size) {
|
||||
// if assignment is empty then clear data and don't bother creating new
|
||||
@ -178,6 +170,24 @@ namespace RNS {
|
||||
}
|
||||
void appendHex(const char* hex);
|
||||
|
||||
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;
|
||||
Bytes mid(size_t pos, size_t len) const;
|
||||
inline Bytes left(size_t len) const { if (!_data) return NONE; if (len > size()) len = size(); return {data(), len}; }
|
||||
inline Bytes right(size_t len) const { if (!_data) return NONE; if (len > size()) len = size(); return {data() + (size() - len), len}; }
|
||||
|
||||
inline uint8_t *writable(size_t size) {
|
||||
newData(size);
|
||||
return _data->data();
|
||||
}
|
||||
|
||||
private:
|
||||
SharedData _data;
|
||||
mutable bool _owner = true;
|
||||
|
@ -55,6 +55,7 @@ namespace RNS { namespace Cryptography {
|
||||
// create random private key
|
||||
Ed25519::generatePrivateKey(_privateKey.writable(32));
|
||||
}
|
||||
// derive public key from private key
|
||||
Ed25519::derivePublicKey(_publicKey.writable(32), _privateKey.data());
|
||||
}
|
||||
~Ed25519PrivateKey() {}
|
||||
|
@ -1,11 +1,12 @@
|
||||
#include "Hashes.h"
|
||||
|
||||
#include "../Log.h"
|
||||
#include "../Bytes.h"
|
||||
|
||||
#include <SHA256.h>
|
||||
#include <SHA512.h>
|
||||
|
||||
using namespace RNS::Cryptography;
|
||||
using namespace RNS;
|
||||
|
||||
/*
|
||||
The SHA primitives are abstracted here to allow platform-
|
||||
@ -14,18 +15,23 @@ 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) {
|
||||
Bytes RNS::Cryptography::sha256(const Bytes &data) {
|
||||
//extreme("Cryptography::sha256: data: " + data.toHex() );
|
||||
SHA256 digest;
|
||||
digest.reset();
|
||||
digest.update(data, data_len);
|
||||
digest.finalize(hash, 32);
|
||||
digest.update(data.data(), data.size());
|
||||
Bytes hash;
|
||||
digest.finalize(hash.writable(32), 32);
|
||||
//extreme("Cryptography::sha256: hash: " + hash.toHex() );
|
||||
return hash;
|
||||
}
|
||||
|
||||
void RNS::Cryptography::sha512(uint8_t *hash, const uint8_t *data, uint16_t data_len) {
|
||||
Bytes RNS::Cryptography::sha512(const Bytes &data) {
|
||||
SHA512 digest;
|
||||
digest.reset();
|
||||
digest.update(data, data_len);
|
||||
digest.finalize(hash, 64);
|
||||
digest.update(data.data(), data.size());
|
||||
Bytes hash;
|
||||
digest.finalize(hash.writable(64), 64);
|
||||
//extreme("Cryptography::sha512: hash: " + hash.toHex() );
|
||||
return hash;
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Bytes.h"
|
||||
|
||||
#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);
|
||||
Bytes sha256(const Bytes &data);
|
||||
Bytes sha512(const Bytes &data);
|
||||
|
||||
} }
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include "Bytes.h"
|
||||
|
||||
#include <Curve25519.h>
|
||||
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
|
||||
@ -10,29 +12,41 @@ namespace RNS { namespace Cryptography {
|
||||
class X25519PublicKey {
|
||||
|
||||
public:
|
||||
/*
|
||||
X25519PublicKey(const Bytes &x) {
|
||||
_x = x;
|
||||
}
|
||||
*/
|
||||
X25519PublicKey(const Bytes &publicKey) {
|
||||
_publicKey = publicKey;
|
||||
}
|
||||
~X25519PublicKey() {}
|
||||
|
||||
using Ptr = std::shared_ptr<X25519PublicKey>;
|
||||
|
||||
public:
|
||||
// creates a new instance with specified seed
|
||||
/*
|
||||
static inline Ptr from_public_bytes(const Bytes &data) {
|
||||
//return Ptr(new X25519PublicKey(_unpack_number(data)));
|
||||
// MOCK
|
||||
return Ptr(new X25519PublicKey(nullptr));
|
||||
return Ptr(new X25519PublicKey(_unpack_number(data)));
|
||||
}
|
||||
*/
|
||||
static inline Ptr from_public_bytes(const Bytes &publicKey) {
|
||||
return Ptr(new X25519PublicKey(publicKey));
|
||||
}
|
||||
|
||||
/*
|
||||
Bytes public_bytes() {
|
||||
//return _pack_number(_x);
|
||||
// MOCK
|
||||
return nullptr;
|
||||
return _pack_number(_x);
|
||||
}
|
||||
*/
|
||||
Bytes public_bytes() {
|
||||
return _publicKey;
|
||||
}
|
||||
|
||||
private:
|
||||
Bytes _x;
|
||||
//Bytes _x;
|
||||
Bytes _publicKey;
|
||||
|
||||
};
|
||||
|
||||
@ -47,43 +61,74 @@ namespace RNS { namespace Cryptography {
|
||||
const uint8_t T_MAX = 0;
|
||||
|
||||
public:
|
||||
/*
|
||||
X25519PrivateKey(const Bytes &a) {
|
||||
_a = a;
|
||||
}
|
||||
*/
|
||||
X25519PrivateKey(const Bytes &privateKey) {
|
||||
if (privateKey) {
|
||||
// use specified private key
|
||||
_privateKey = privateKey;
|
||||
// similar to derive public key from private key
|
||||
// second param "f" is secret
|
||||
//eval(uint8_t result[32], const uint8_t s[32], const uint8_t x[32])
|
||||
// derive public key from private key
|
||||
Curve25519::eval(_publicKey.writable(32), _privateKey.data(), 0);
|
||||
}
|
||||
else {
|
||||
// create random private key and derive public key
|
||||
// second param "f" is secret
|
||||
//dh1(uint8_t k[32], uint8_t f[32])
|
||||
Curve25519::dh1(_publicKey.writable(32), _privateKey.writable(32));
|
||||
}
|
||||
}
|
||||
~X25519PrivateKey() {}
|
||||
|
||||
using Ptr = std::shared_ptr<X25519PrivateKey>;
|
||||
|
||||
public:
|
||||
// creates a new instance with a random seed
|
||||
/*
|
||||
static inline Ptr generate() {
|
||||
//return from_private_bytes(os.urandom(32));
|
||||
// MOCK
|
||||
return from_private_bytes(nullptr);
|
||||
return from_private_bytes(os.urandom(32));
|
||||
}
|
||||
*/
|
||||
static inline Ptr generate() {
|
||||
return from_private_bytes(Bytes::NONE);
|
||||
}
|
||||
|
||||
// creates a new instance with specified seed
|
||||
/*
|
||||
static inline Ptr from_private_bytes(const Bytes &data) {
|
||||
//return Ptr(new X25519PrivateKey(_fix_secret(_unpack_number(data))));
|
||||
// MOCK
|
||||
return Ptr(new X25519PrivateKey(nullptr));
|
||||
return Ptr(new X25519PrivateKey(_fix_secret(_unpack_number(data))));
|
||||
}
|
||||
*/
|
||||
static inline Ptr from_private_bytes(const Bytes &privateKey) {
|
||||
return Ptr(new X25519PrivateKey(privateKey));
|
||||
}
|
||||
|
||||
/*
|
||||
inline Bytes private_bytes() {
|
||||
//return _pack_number(_a);
|
||||
// MOCK
|
||||
return nullptr;
|
||||
return _pack_number(_a);
|
||||
}
|
||||
*/
|
||||
inline Bytes private_bytes() {
|
||||
return _privateKey;
|
||||
}
|
||||
|
||||
// creates a new instance of public key for this private key
|
||||
/*
|
||||
inline X25519PublicKey::Ptr public_key() {
|
||||
//return X25519PublicKey::from_public_bytes(_pack_number(_raw_curve25519(9, _a)));
|
||||
// MOCK
|
||||
return X25519PublicKey::from_public_bytes(nullptr);
|
||||
return X25519PublicKey::from_public_bytes(_pack_number(_raw_curve25519(9, _a)));
|
||||
}
|
||||
*/
|
||||
inline X25519PublicKey::Ptr public_key() {
|
||||
return X25519PublicKey::from_public_bytes(_publicKey);
|
||||
}
|
||||
|
||||
inline Bytes exchange(const Bytes &peer_public_key) {
|
||||
/*
|
||||
inline Bytes exchange(const Bytes &peer_public_key) {
|
||||
if isinstance(peer_public_key, bytes):
|
||||
peer_public_key = X25519PublicKey.from_public_bytes(peer_public_key)
|
||||
|
||||
@ -119,12 +164,18 @@ namespace RNS { namespace Cryptography {
|
||||
X25519PrivateKey.T_MAX = duration
|
||||
|
||||
return shared
|
||||
}
|
||||
*/
|
||||
return nullptr;
|
||||
inline Bytes exchange(const Bytes &peer_public_key) {
|
||||
Bytes sharedKey(peer_public_key);
|
||||
Curve25519::dh2(sharedKey.writable(32), _privateKey.writable(32));
|
||||
return sharedKey;
|
||||
}
|
||||
|
||||
private:
|
||||
Bytes _a;
|
||||
//Bytes _a;
|
||||
Bytes _privateKey;
|
||||
Bytes _publicKey;
|
||||
|
||||
};
|
||||
|
||||
|
@ -272,15 +272,15 @@ 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)
|
||||
return _object->_identity.encrypt(data);
|
||||
}
|
||||
|
||||
/*
|
||||
if (_object->_type == Destination::GROUP {
|
||||
if hasattr(self, "prv") and self.prv != None:
|
||||
try:
|
||||
@ -292,9 +292,8 @@ Bytes Destination::encrypt(const Bytes &data) {
|
||||
raise ValueError("No private key held by GROUP destination. Did you create or load one?")
|
||||
}
|
||||
*/
|
||||
|
||||
// MOCK
|
||||
return data;
|
||||
return Bytes::NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -307,15 +306,15 @@ 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);
|
||||
return _object->_identity.decrypt(data);
|
||||
}
|
||||
|
||||
/*
|
||||
if (_object->_type == Destination::GROUP) {
|
||||
if hasattr(self, "prv") and self.prv != None:
|
||||
try:
|
||||
@ -327,9 +326,8 @@ Bytes Destination::decrypt(const Bytes &data) {
|
||||
raise ValueError("No private key held by GROUP destination. Did you create or load one?")
|
||||
}
|
||||
*/
|
||||
|
||||
// MOCK
|
||||
return data;
|
||||
return Bytes::NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -343,5 +341,5 @@ Bytes Destination::sign(const Bytes &message) {
|
||||
if (_object->_type == Destination::SINGLE && _object->_identity) {
|
||||
return _object->_identity.sign(message);
|
||||
}
|
||||
return nullptr;
|
||||
return Bytes::NONE;
|
||||
}
|
||||
|
173
src/Identity.cpp
173
src/Identity.cpp
@ -1,7 +1,10 @@
|
||||
#include "Identity.h"
|
||||
|
||||
#include "Reticulum.h"
|
||||
#include "Packet.h"
|
||||
#include "Log.h"
|
||||
#include "Cryptography/Hashes.h"
|
||||
#include "Cryptography/X25519.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@ -20,7 +23,7 @@ void Identity::createKeys() {
|
||||
|
||||
_object->_prv = Cryptography::X25519PrivateKey::generate();
|
||||
_object->_prv_bytes = _object->_prv->private_bytes();
|
||||
debug("Identity::createKeys: prv bytes: " + _object->_prv_bytes.toHex());
|
||||
debug("Identity::createKeys: prv bytes: " + _object->_prv_bytes.toHex());
|
||||
|
||||
_object->_sig_prv = Cryptography::Ed25519PrivateKey::generate();
|
||||
_object->_sig_prv_bytes = _object->_sig_prv->private_bytes();
|
||||
@ -28,7 +31,7 @@ void Identity::createKeys() {
|
||||
|
||||
_object->_pub = _object->_prv->public_key();
|
||||
_object->_pub_bytes = _object->_pub->public_bytes();
|
||||
debug("Identity::createKeys: pub bytes: " + _object->_pub_bytes.toHex());
|
||||
debug("Identity::createKeys: pub bytes: " + _object->_pub_bytes.toHex());
|
||||
|
||||
_object->_sig_pub = _object->_sig_prv->public_key();
|
||||
_object->_sig_pub_bytes = _object->_sig_pub->public_bytes();
|
||||
@ -45,18 +48,122 @@ void Identity::createKeys() {
|
||||
Bytes Identity::get_public_key() {
|
||||
assert(_object);
|
||||
return _object->_pub_bytes + _object->_sig_pub_bytes;
|
||||
// MOCK
|
||||
return "abc123";
|
||||
}
|
||||
|
||||
void Identity::update_hashes() {
|
||||
assert(_object);
|
||||
_object->_hash = truncated_hash(get_public_key());
|
||||
debug("Identity::update_hashes: hash: " + _object->_hash.toHex());
|
||||
debug("Identity::update_hashes: hash: " + _object->_hash.toHex());
|
||||
_object->_hexhash = _object->_hash.toHex();
|
||||
debug("Identity::update_hashes: hexhash: " + _object->_hexhash);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
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) {
|
||||
return Cryptography::sha256(data);
|
||||
}
|
||||
|
||||
/*
|
||||
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) {
|
||||
return full_hash(data).right(TRUNCATED_HASHLENGTH/8);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Encrypts information for the identity.
|
||||
|
||||
:param plaintext: The plaintext to be encrypted as *bytes*.
|
||||
:returns: Ciphertext token as *bytes*.
|
||||
:raises: *KeyError* if the instance does not hold a public key.
|
||||
*/
|
||||
Bytes Identity::encrypt(const Bytes &plaintext) {
|
||||
assert(_object);
|
||||
if (_object->_pub) {
|
||||
Cryptography::X25519PrivateKey::Ptr ephemeral_key = Cryptography::X25519PrivateKey::generate();
|
||||
Bytes ephemeral_pub_bytes = ephemeral_key->public_key()->public_bytes();
|
||||
|
||||
/*
|
||||
Bytes shared_key = ephemeral_key->exchange(_object->_pub);
|
||||
|
||||
Bytes derived_key = RNS.Cryptography.hkdf(
|
||||
length=32,
|
||||
derive_from=shared_key,
|
||||
salt=get_salt(),
|
||||
context=get_context(),
|
||||
)
|
||||
|
||||
fernet = Fernet(derived_key)
|
||||
ciphertext = fernet.encrypt(plaintext)
|
||||
|
||||
return ephemeral_pub_bytes + ciphertext;
|
||||
*/
|
||||
// MOCK
|
||||
return Bytes::NONE;
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Encryption failed because identity does not hold a public key");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Decrypts information for the identity.
|
||||
|
||||
:param ciphertext: The ciphertext to be decrypted as *bytes*.
|
||||
:returns: Plaintext as *bytes*, or *None* if decryption fails.
|
||||
:raises: *KeyError* if the instance does not hold a private key.
|
||||
*/
|
||||
Bytes Identity::decrypt(const Bytes &ciphertext_token) {
|
||||
assert(_object);
|
||||
if (_object->_prv) {
|
||||
if (ciphertext_token.size() > Identity::KEYSIZE/8/2) {
|
||||
Bytes plaintext;
|
||||
try {
|
||||
Bytes peer_pub_bytes = ciphertext_token.right(Identity::KEYSIZE/8/2);
|
||||
Cryptography::X25519PublicKey::Ptr peer_pub = Cryptography::X25519PublicKey::from_public_bytes(peer_pub_bytes);
|
||||
|
||||
/*
|
||||
Bytes shared_key = _object->_prv->exchange(peer_pub);
|
||||
|
||||
Bytes derived_key = RNS.Cryptography.hkdf(
|
||||
length=32,
|
||||
derive_from=shared_key,
|
||||
salt=get_salt(),
|
||||
context=get_context(),
|
||||
)
|
||||
|
||||
fernet = Fernet(derived_key)
|
||||
ciphertext = ciphertext_token[Identity.KEYSIZE//8//2:]
|
||||
plaintext = fernet.decrypt(ciphertext)
|
||||
*/
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
debug("Decryption by " + _object->_hash.toHex() + " failed: " + e.what());
|
||||
}
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
else {
|
||||
debug("Decryption failed because the token size was invalid.");
|
||||
return Bytes::NONE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Decryption failed because identity does not hold a private key");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Signs information by the identity.
|
||||
|
||||
@ -70,7 +177,7 @@ Bytes Identity::sign(const Bytes &message) {
|
||||
try {
|
||||
return _object->_sig_prv->sign(message);
|
||||
}
|
||||
catch (std::exception e) {
|
||||
catch (std::exception &e) {
|
||||
error("The identity " + toString() + " could not sign the requested message. The contained exception was: " + e.what());
|
||||
throw e;
|
||||
}
|
||||
@ -80,29 +187,45 @@ Bytes Identity::sign(const Bytes &message) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get a SHA-256 hash of passed data.
|
||||
Validates the signature of a signed message.
|
||||
|
||||
:param data: Data to be hashed as *bytes*.
|
||||
:returns: SHA-256 hash as *bytes*
|
||||
:param signature: The signature to be validated as *bytes*.
|
||||
:param message: The message to be validated as *bytes*.
|
||||
:returns: True if the signature is valid, otherwise False.
|
||||
:raises: *KeyError* if the instance does not hold a public key.
|
||||
*/
|
||||
/*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;
|
||||
bool Identity::validate(const Bytes &signature, const Bytes &message) {
|
||||
assert(_object);
|
||||
if (_object->_pub) {
|
||||
try {
|
||||
_object->_sig_pub->verify(signature, message);
|
||||
return true;
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Signature validation failed because identity does not hold a public key");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Get a truncated SHA-256 hash of passed data.
|
||||
void Identity::prove(const Packet &packet, const Destination &destination /*= Destination::NONE*/) {
|
||||
assert(_object);
|
||||
Bytes signature(sign(packet._packet_hash));
|
||||
Bytes proof_data;
|
||||
if (RNS::Reticulum::should_use_implicit_proof()) {
|
||||
proof_data = signature;
|
||||
}
|
||||
else {
|
||||
proof_data = packet._packet_hash + signature;
|
||||
}
|
||||
|
||||
: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);
|
||||
//zif (!destination) {
|
||||
//z destination = packet.generate_proof_destination();
|
||||
//z}
|
||||
|
||||
Packet proof(destination, packet.receiving_interface(), proof_data, RNS::Packet::PROOF);
|
||||
proof.send();
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Reticulum.h"
|
||||
// CBA TODO determine why including Destination.h here causes build errors
|
||||
//#include "Destination.h"
|
||||
#include "Log.h"
|
||||
#include "Bytes.h"
|
||||
#include "Cryptography/Fernet.h"
|
||||
@ -13,6 +15,9 @@
|
||||
|
||||
namespace RNS {
|
||||
|
||||
class Destination;
|
||||
class Packet;
|
||||
|
||||
class Identity {
|
||||
|
||||
public:
|
||||
@ -66,8 +71,15 @@ namespace RNS {
|
||||
|
||||
static Bytes full_hash(const Bytes &data);
|
||||
static Bytes truncated_hash(const Bytes &data);
|
||||
Bytes sign(const Bytes &message);
|
||||
|
||||
Bytes encrypt(const Bytes &plaintext);
|
||||
Bytes decrypt(const Bytes &ciphertext_token);
|
||||
Bytes sign(const Bytes &message);
|
||||
bool validate(const Bytes &signature, const Bytes &message);
|
||||
//void prove(const Packet &packet, const Destination &destination = Destination::NONE);
|
||||
void prove(const Packet &packet, const Destination &destination);
|
||||
|
||||
// getters/setters
|
||||
inline Bytes encryptionPrivateKey() const { assert(_object); return _object->_prv_bytes; }
|
||||
inline Bytes signingPrivateKey() const { assert(_object); return _object->_sig_prv_bytes; }
|
||||
inline Bytes encryptionPublicKey() const { assert(_object); return _object->_prv_bytes; }
|
||||
|
@ -5,10 +5,10 @@
|
||||
using namespace RNS;
|
||||
|
||||
Interface::Interface() {
|
||||
log("Interface object created", LOG_EXTREME);
|
||||
extreme("Interface object created");
|
||||
}
|
||||
|
||||
Interface::~Interface() {
|
||||
log("Interface object destroyed", LOG_EXTREME);
|
||||
extreme("Interface object destroyed");
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
//#include <Arduino.h>
|
||||
#include "../Log.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace RNS {
|
||||
|
||||
class Interface {
|
||||
|
||||
public:
|
||||
enum NoneConstructor {
|
||||
NONE
|
||||
};
|
||||
|
||||
public:
|
||||
// Interface mode definitions
|
||||
enum modes {
|
||||
@ -22,9 +29,33 @@ namespace RNS {
|
||||
//zDISCOVER_PATHS_FOR = [MODE_ACCESS_POINT, MODE_GATEWAY]
|
||||
|
||||
public:
|
||||
Interface(NoneConstructor none) {
|
||||
extreme("Interface object NONE created");
|
||||
}
|
||||
Interface(const Interface &interface) : _object(interface._object) {
|
||||
extreme("Interface object copy created");
|
||||
}
|
||||
Interface();
|
||||
~Interface();
|
||||
|
||||
inline Interface& operator = (const Interface &interface) {
|
||||
_object = interface._object;
|
||||
extreme("Interface 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:
|
||||
class Object {
|
||||
private:
|
||||
|
||||
|
||||
friend class Interface;
|
||||
};
|
||||
std::shared_ptr<Object> _object;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -8,8 +8,8 @@
|
||||
|
||||
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);
|
||||
//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)) {
|
||||
Packet::Packet(const Destination &destination, const Interface &attached_interface, 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*/, bool create_receipt /*= true*/) : _object(new Object(destination, attached_interface)) {
|
||||
|
||||
if (_object->_destination) {
|
||||
// CBA TODO handle NONE
|
||||
@ -52,7 +52,6 @@ Packet::Packet(const Destination &destination, const Bytes &data, types packet_t
|
||||
_fromPacked = true;
|
||||
_create_receipt = false;
|
||||
}
|
||||
_attached_interface = attached_interface;
|
||||
extreme("Packet object created");
|
||||
}
|
||||
|
||||
@ -427,8 +426,8 @@ bool Packet::unpack() {
|
||||
_packed = false;
|
||||
update_hash();
|
||||
}
|
||||
catch (std::exception& ex) {
|
||||
log(std::string("Received malformed packet, dropping it. The contained exception was: ") + ex.what(), LOG_EXTREME);
|
||||
catch (std::exception& e) {
|
||||
error(std::string("Received malformed packet, dropping it. The contained exception was: ") + e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -530,3 +529,10 @@ Bytes Packet::get_hashable_part() {
|
||||
hashable_part.append(_data-Reticulum::DESTINATION_LENGTH-1, _data_len+Reticulum::DESTINATION_LENGTH+1);
|
||||
return hashable_part;
|
||||
}
|
||||
|
||||
// Generates a special destination that allows Reticulum
|
||||
// to direct the proof back to the proved packet's sender
|
||||
//ProofDestination &Packet::generate_proof_destination() {
|
||||
// return ProofDestination();
|
||||
//}
|
||||
|
||||
|
39
src/Packet.h
39
src/Packet.h
@ -14,6 +14,7 @@ namespace RNS {
|
||||
|
||||
class Packet;
|
||||
class PacketProof;
|
||||
class ProofDestination;
|
||||
class PacketReceipt;
|
||||
class PacketReceiptCallbacks;
|
||||
|
||||
@ -84,7 +85,9 @@ namespace RNS {
|
||||
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(const Destination &destination, const Interface &attached_interface, 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, bool create_receipt = true);
|
||||
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, bool create_receipt = true) : Packet(destination, Interface::NONE, data, DATA, CONTEXT_NONE, Transport::BROADCAST, HEADER_1, nullptr, create_receipt) {
|
||||
}
|
||||
Packet(NoneConstructor none) {
|
||||
extreme("Packet NONE object created");
|
||||
}
|
||||
@ -110,16 +113,6 @@ namespace RNS {
|
||||
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();
|
||||
@ -130,8 +123,12 @@ namespace RNS {
|
||||
Bytes get_hash();
|
||||
Bytes getTruncatedHash();
|
||||
Bytes get_hashable_part();
|
||||
//zProofDestination &generate_proof_destination();
|
||||
|
||||
private:
|
||||
// getters/setters
|
||||
inline const Interface& receiving_interface() const { assert(_object); return _object->_receiving_interface; }
|
||||
|
||||
public:
|
||||
types _packet_type;
|
||||
header_types _header_type;
|
||||
context_types _context;
|
||||
@ -151,9 +148,6 @@ namespace RNS {
|
||||
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;
|
||||
|
||||
@ -166,6 +160,21 @@ namespace RNS {
|
||||
uint8_t _header[Reticulum::HEADER_MAXSIZE];
|
||||
uint8_t *_data = _raw + Reticulum::HEADER_MAXSIZE;
|
||||
uint16_t _data_len = 0;
|
||||
|
||||
private:
|
||||
class Object {
|
||||
public:
|
||||
Object(const Destination &destination, const Interface &attached_interface) : _destination(destination), _attached_interface(attached_interface) {}
|
||||
private:
|
||||
|
||||
Destination _destination;
|
||||
|
||||
Interface _attached_interface;
|
||||
Interface _receiving_interface;
|
||||
|
||||
friend class Packet;
|
||||
};
|
||||
std::shared_ptr<Object> _object;
|
||||
};
|
||||
|
||||
|
||||
|
@ -18,6 +18,15 @@ Reticulum::~Reticulum() {
|
||||
extreme("Reticulum object destroyed");
|
||||
}
|
||||
|
||||
/*
|
||||
Returns whether proofs sent are explicit or implicit.
|
||||
|
||||
:returns: True if the current running configuration specifies to use implicit proofs. False if not.
|
||||
*/
|
||||
/*static*/ bool Reticulum::should_use_implicit_proof() {
|
||||
return __use_implicit_proof;
|
||||
}
|
||||
|
||||
void Reticulum::loop() {
|
||||
// Perform random number gnerator housekeeping
|
||||
RNG.loop();
|
||||
|
@ -10,12 +10,6 @@ namespace RNS {
|
||||
|
||||
class Reticulum {
|
||||
|
||||
private:
|
||||
class Object {
|
||||
private:
|
||||
friend class Reticulum;
|
||||
};
|
||||
|
||||
public:
|
||||
enum NoneConstructor {
|
||||
NONE
|
||||
@ -85,6 +79,12 @@ namespace RNS {
|
||||
|
||||
static const uint8_t DESTINATION_LENGTH = TRUNCATED_HASHLENGTH/8; // In bytes
|
||||
|
||||
static const bool __transport_enabled = false;
|
||||
static const bool __use_implicit_proof = true;
|
||||
static const bool __allow_probes = false;
|
||||
|
||||
static const bool panic_on_interface_error = false;
|
||||
|
||||
public:
|
||||
Reticulum();
|
||||
Reticulum(NoneConstructor none) {
|
||||
@ -105,9 +105,14 @@ namespace RNS {
|
||||
}
|
||||
|
||||
public:
|
||||
static bool should_use_implicit_proof();
|
||||
void loop();
|
||||
|
||||
private:
|
||||
class Object {
|
||||
private:
|
||||
friend class Reticulum;
|
||||
};
|
||||
std::shared_ptr<Object> _object;
|
||||
|
||||
};
|
||||
|
@ -10,7 +10,6 @@ void test() {
|
||||
testMap();
|
||||
testBytes();
|
||||
testCowBytes();
|
||||
testObjects();
|
||||
testBytesConversion();
|
||||
|
||||
testReference();
|
||||
|
@ -6,8 +6,6 @@ void testBytes();
|
||||
void testCowBytes();
|
||||
void testBytesConversion();
|
||||
|
||||
void testObjects();
|
||||
|
||||
void testReference();
|
||||
|
||||
void testCrypto();
|
||||
|
@ -105,6 +105,56 @@ void testBytes() {
|
||||
assert(bytes.size() == 11);
|
||||
assert(memcmp(bytes.data(), "Hello World", bytes.size()) == 0);
|
||||
|
||||
// test left in range
|
||||
{
|
||||
RNS::Bytes left(bytes.left(5));
|
||||
RNS::extreme("left: " + left.toString());
|
||||
assert(left.size() == 5);
|
||||
assert(memcmp(left.data(), "Hello", left.size()) == 0);
|
||||
}
|
||||
// test left oob
|
||||
{
|
||||
RNS::Bytes left(bytes.left(20));
|
||||
RNS::extreme("oob left: " + left.toString());
|
||||
assert(left.size() == 11);
|
||||
assert(memcmp(left.data(), "Hello World", left.size()) == 0);
|
||||
}
|
||||
// test right in range
|
||||
{
|
||||
RNS::Bytes right(bytes.right(5));
|
||||
RNS::extreme("right: " + right.toString());
|
||||
assert(right.size() == 5);
|
||||
assert(memcmp(right.data(), "World", right.size()) == 0);
|
||||
}
|
||||
// test right oob
|
||||
{
|
||||
RNS::Bytes right(bytes.right(20));
|
||||
RNS::extreme("oob right: " + right.toString());
|
||||
assert(right.size() == 11);
|
||||
assert(memcmp(right.data(), "Hello World", right.size()) == 0);
|
||||
}
|
||||
// test mid in range
|
||||
{
|
||||
RNS::Bytes mid(bytes.mid(3, 5));
|
||||
RNS::extreme("mid: " + mid.toString());
|
||||
assert(mid.size() == 5);
|
||||
assert(memcmp(mid.data(), "lo Wo", mid.size()) == 0);
|
||||
}
|
||||
// test mid oob pos
|
||||
{
|
||||
RNS::Bytes mid(bytes.mid(20, 5));
|
||||
RNS::extreme("oob pos mid: " + mid.toString());
|
||||
assert(!mid);
|
||||
assert(mid.size() == 0);
|
||||
}
|
||||
// test mid oob pos
|
||||
{
|
||||
RNS::Bytes mid(bytes.mid(3, 20));
|
||||
RNS::extreme("oob len mid: " + mid.toString());
|
||||
assert(mid.size() == 8);
|
||||
assert(memcmp(mid.data(), "lo World", mid.size()) == 0);
|
||||
}
|
||||
|
||||
// stream into empty bytes
|
||||
{
|
||||
RNS::Bytes strmbuf;
|
||||
|
@ -25,7 +25,7 @@ void testCrypto() {
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(testObjects);
|
||||
RUN_TEST(testCrypto);
|
||||
return UNITY_END();
|
||||
}
|
||||
*/
|
||||
|
@ -23,7 +23,7 @@ void testReference() {
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(testObjects);
|
||||
RUN_TEST(testReference);
|
||||
return UNITY_END();
|
||||
}
|
||||
*/
|
||||
|
@ -38,6 +38,6 @@ Transport::~Transport() {
|
||||
}
|
||||
|
||||
/*static*/ bool Transport::outbound(const Packet &packet) {
|
||||
// mock
|
||||
// MOCK
|
||||
return true;
|
||||
}
|
||||
|
57
src/main.cpp
57
src/main.cpp
@ -39,45 +39,50 @@ void setup() {
|
||||
Serial.print("Hello from T-Beam on PlatformIO!\n");
|
||||
#endif
|
||||
|
||||
try {
|
||||
|
||||
#ifndef NDEBUG
|
||||
//RNS::loglevel(RNS::LOG_WARNING);
|
||||
RNS::loglevel(RNS::LOG_EXTREME);
|
||||
//test();
|
||||
testCrypto();
|
||||
return;
|
||||
RNS::loglevel(RNS::LOG_WARNING);
|
||||
//RNS::loglevel(RNS::LOG_EXTREME);
|
||||
test();
|
||||
#endif
|
||||
|
||||
//std::stringstream test;
|
||||
// !!! just adding this single stringstream alone (not even using it) adds a whopping 17.1% !!!
|
||||
// !!! JUST SAY NO TO STRINGSTREAM !!!
|
||||
//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);
|
||||
RNS::loglevel(RNS::LOG_EXTREME);
|
||||
|
||||
// 18.5% completely empty program
|
||||
// 18.5% completely empty program
|
||||
|
||||
// 21.8% baseline here with serial
|
||||
// 21.8% baseline here with serial
|
||||
|
||||
RNS::Reticulum reticulum;
|
||||
// 21.9% (+0.1%)
|
||||
RNS::Reticulum reticulum;
|
||||
// 21.9% (+0.1%)
|
||||
|
||||
RNS::Identity identity;
|
||||
// 22.6% (+0.7%)
|
||||
RNS::Identity identity;
|
||||
// 22.6% (+0.7%)
|
||||
|
||||
RNS::Destination destination(identity, RNS::Destination::IN, RNS::Destination::SINGLE, "test", "context");
|
||||
// 23.0% (+0.4%)
|
||||
RNS::Destination destination(identity, RNS::Destination::IN, RNS::Destination::SINGLE, "test", "context");
|
||||
// 23.0% (+0.4%)
|
||||
|
||||
//destination.announce(RNS::bytesFromString(fruits[rand() % 7]));
|
||||
// test path
|
||||
destination.announce(RNS::bytesFromString(fruits[rand() % 7]), true, nullptr, RNS::bytesFromString("test_tag"));
|
||||
// 23.9% (+0.8%)
|
||||
//destination.announce(RNS::bytesFromString(fruits[rand() % 7]));
|
||||
// test path
|
||||
destination.announce(RNS::bytesFromString(fruits[rand() % 7]), true, nullptr, RNS::bytesFromString("test_tag"));
|
||||
// 23.9% (+0.8%)
|
||||
|
||||
//zdestination.set_proof_strategy(RNS::Destination::PROVE_ALL);
|
||||
//zdestination.set_proof_strategy(RNS::Destination::PROVE_ALL);
|
||||
|
||||
//zannounce_handler = ExampleAnnounceHandler(
|
||||
//z aspect_filter="example_utilities.announcesample.fruits";
|
||||
//z)
|
||||
//zannounce_handler = ExampleAnnounceHandler(
|
||||
//z aspect_filter="example_utilities.announcesample.fruits";
|
||||
//z)
|
||||
|
||||
//zRNS::Transport.register_announce_handler(announce_handler);
|
||||
//zRNS::Transport.register_announce_handler(announce_handler);
|
||||
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
RNS::error(std::string("!!! Exception in main: ") + e.what() + " !!!");
|
||||
}
|
||||
|
||||
#ifndef NATIVE
|
||||
Serial.print("Goodbye from T-Beam on PlatformIO!\n");
|
||||
|
Loading…
Reference in New Issue
Block a user