WIP update

Implemented AES.
Implemented AES-CBC.
Implemented HKDF.
Implemented HMAC.
Implemented PKCS7.
Implemented Fernet.
Added extensive logging and successfully tested ability to encrypt and
decrypt a packet.
This commit is contained in:
attermann 2023-10-08 23:59:43 -06:00
parent 4b062aa485
commit f9e3a22911
28 changed files with 1487 additions and 328 deletions

View File

@ -28,7 +28,7 @@ build_flags =
-Isrc -Isrc
-DNATIVE -DNATIVE
lib_deps = lib_deps =
; operatorfoundation/Crypto@^0.4.0 ; rweather/Crypto@^0.4.0
lib_compat_mode = off lib_compat_mode = off
[env:ttgo-t-beam] [env:ttgo-t-beam]
@ -43,4 +43,4 @@ build_flags =
-Wno-format -Wno-format
-Isrc -Isrc
lib_deps = lib_deps =
operatorfoundation/Crypto@^0.4.0 rweather/Crypto@^0.4.0

View File

@ -34,15 +34,19 @@ int8_t Bytes::compare(const Bytes &bytes) const {
else if (*_data < *(bytes._data)) { else if (*_data < *(bytes._data)) {
return -1; return -1;
} }
else { else if (*_data > *(bytes._data)) {
return 1; return 1;
} }
else {
return 0;
}
} }
void Bytes::assignHex(const char* hex) { void Bytes::assignHex(const char* hex) {
// if assignment is empty then clear data and don't bother creating new // if assignment is empty then clear data and don't bother creating new
if (hex == nullptr || hex[0] == 0) { if (hex == nullptr || hex[0] == 0) {
_data = nullptr; _data = nullptr;
_owner = true;
return; return;
} }
newData(); newData();
@ -66,8 +70,21 @@ void Bytes::appendHex(const char* hex) {
} }
} }
char const hex_upper_chars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; const char 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' }; const char hex_lower_chars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
std::string RNS::hexFromByte(uint8_t byte, bool upper /*= true*/) {
std::string hex;
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;
}
std::string Bytes::toHex(bool upper /*= true*/) const { std::string Bytes::toHex(bool upper /*= true*/) const {
if (!_data) { if (!_data) {
@ -87,12 +104,21 @@ std::string Bytes::toHex(bool upper /*= true*/) const {
return hex; return hex;
} }
Bytes Bytes::mid(size_t pos, size_t len) const { // mid
if (!_data || pos >= size()) { Bytes Bytes::mid(size_t beginpos, size_t len) const {
if (!_data || beginpos >= size()) {
return NONE; return NONE;
} }
if ((pos + len) >= size()) { if ((beginpos + len) >= size()) {
len = (size() - pos); len = (size() - beginpos);
} }
return {data() + pos, len}; return {data() + beginpos, len};
}
// to end
Bytes Bytes::mid(size_t beginpos) const {
if (!_data || beginpos >= size()) {
return NONE;
}
return {data() + beginpos, size() - beginpos};
} }

View File

@ -104,6 +104,11 @@ namespace RNS {
void ownData(); void ownData();
public: public:
inline void clear() {
_data = nullptr;
_owner = true;
}
inline void assign(const Bytes& bytes) { inline void assign(const Bytes& bytes) {
#ifdef COW #ifdef COW
_data = bytes.shareData(); _data = bytes.shareData();
@ -112,6 +117,7 @@ namespace RNS {
// if assignment is empty then clear data and don't bother creating new // if assignment is empty then clear data and don't bother creating new
if (bytes.size() <= 0) { if (bytes.size() <= 0) {
_data = nullptr; _data = nullptr;
_owner = true;
return; return;
} }
newData(); newData();
@ -122,6 +128,7 @@ namespace RNS {
// if assignment is empty then clear data and don't bother creating new // if assignment is empty then clear data and don't bother creating new
if (chunk == nullptr || size <= 0) { if (chunk == nullptr || size <= 0) {
_data = nullptr; _data = nullptr;
_owner = true;
return; return;
} }
newData(); newData();
@ -131,6 +138,7 @@ namespace RNS {
// if assignment is empty then clear data and don't bother creating new // if assignment is empty then clear data and don't bother creating new
if (string == nullptr || string[0] == 0) { if (string == nullptr || string[0] == 0) {
_data = nullptr; _data = nullptr;
_owner = true;
return; return;
} }
newData(); newData();
@ -170,6 +178,20 @@ namespace RNS {
} }
void appendHex(const char* hex); void appendHex(const char* hex);
inline void resize(size_t newsize) {
// if size is unchanged then do nothing
if (newsize == size()) {
return;
}
ownData();
_data->resize(newsize);
}
inline uint8_t *writable(size_t size) {
newData(size);
return _data->data();
}
public: public:
int8_t compare(const Bytes &bytes) const; int8_t compare(const Bytes &bytes) const;
inline size_t size() const { if (!_data) return 0; return _data->size(); } inline size_t size() const { if (!_data) return 0; return _data->size(); }
@ -179,15 +201,11 @@ namespace RNS {
inline std::string toString() const { if (!_data) return ""; return {(const char*)data(), size()}; } inline std::string toString() const { if (!_data) return ""; return {(const char*)data(), size()}; }
std::string toHex(bool upper = true) const; std::string toHex(bool upper = true) const;
Bytes mid(size_t pos, size_t len) const; Bytes mid(size_t beginpos, size_t len) const;
Bytes mid(size_t beginpos) const;
inline Bytes left(size_t len) const { if (!_data) return NONE; if (len > size()) len = size(); return {data(), len}; } 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 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: private:
SharedData _data; SharedData _data;
mutable bool _owner = true; mutable bool _owner = true;
@ -195,14 +213,16 @@ namespace RNS {
}; };
// following array function doesn't work without size since it's past as a pointer to the array sizeof() is of the pointer // 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)); } //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); } //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}; } 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)); } //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)}; } inline Bytes bytesFromString(const char *str) { return {(uint8_t*)str, strlen(str)}; }
//zstatic inline Bytes bytesFromInt(const int) { return {(uint8_t*)str, strlen(str)}; } //zinline 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 std::string stringFromBytes(const Bytes& bytes) { return bytes.toString(); }
inline std::string hexFromBytes(const Bytes& bytes) { return bytes.toHex(); }
std::string hexFromByte(uint8_t byte, bool upper = true);
} }

50
src/Cryptography/AES.h Normal file
View File

@ -0,0 +1,50 @@
#pragma once
#include "CBC.h"
#include "../Bytes.h"
#include <AES.h>
namespace RNS { namespace Cryptography {
class AES_128_CBC {
public:
static inline Bytes encrypt(const Bytes &plaintext, const Bytes &key, const Bytes &iv) {
CBC<AES128> cbc;
cbc.setKey(key.data(), key.size());
cbc.setIV(iv.data(), iv.size());
Bytes ciphertext;
cbc.encrypt(ciphertext.writable(plaintext.size()), plaintext.data(), plaintext.size());
return ciphertext;
}
static inline Bytes decrypt(const Bytes &ciphertext, const Bytes &key, const Bytes &iv) {
CBC<AES128> cbc;
cbc.setKey(key.data(), key.size());
cbc.setIV(iv.data(), iv.size());
Bytes plaintext;
cbc.decrypt(plaintext.writable(ciphertext.size()), ciphertext.data(), ciphertext.size());
return plaintext;
}
// EXPERIMENTAL - overwrites passed buffer
static inline void inplace_encrypt(Bytes &plaintext, const Bytes &key, const Bytes &iv) {
CBC<AES128> cbc;
cbc.setKey(key.data(), key.size());
cbc.setIV(iv.data(), iv.size());
cbc.encrypt((uint8_t*)plaintext.data(), plaintext.data(), plaintext.size());
}
// EXPERIMENTAL - overwrites passed buffer
static inline void inplace_decrypt(Bytes &ciphertext, const Bytes &key, const Bytes &iv) {
CBC<AES128> cbc;
cbc.setKey(key.data(), key.size());
cbc.setIV(iv.data(), iv.size());
cbc.decrypt((uint8_t*)ciphertext.data(), ciphertext.data(), ciphertext.size());
}
};
} }

171
src/Cryptography/CBC.cpp Normal file
View File

@ -0,0 +1,171 @@
/*
* Copyright (C) 2015 Southern Storm Software, Pty Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "CBC.h"
#include "Crypto.h"
#include <string.h>
/**
* \class CBCCommon CBC.h <CBC.h>
* \brief Concrete base class to assist with implementing CBC for
* 128-bit block ciphers.
*
* Reference: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
*
* \sa CBC
*/
/**
* \brief Constructs a new cipher in CBC mode.
*
* This constructor should be followed by a call to setBlockCipher().
*/
CBCCommon::CBCCommon()
: blockCipher(0)
, posn(16)
{
}
/**
* \brief Destroys this cipher object after clearing sensitive information.
*/
CBCCommon::~CBCCommon()
{
clean(iv);
clean(temp);
}
size_t CBCCommon::keySize() const
{
return blockCipher->keySize();
}
size_t CBCCommon::ivSize() const
{
return 16;
}
bool CBCCommon::setKey(const uint8_t *key, size_t len)
{
// Verify the cipher's block size, just in case.
if (blockCipher->blockSize() != 16)
return false;
// Set the key on the underlying block cipher.
return blockCipher->setKey(key, len);
}
bool CBCCommon::setIV(const uint8_t *iv, size_t len)
{
if (len != 16)
return false;
memcpy(this->iv, iv, 16);
posn = 16;
return true;
}
void CBCCommon::encrypt(uint8_t *output, const uint8_t *input, size_t len)
{
uint8_t posn;
while (len >= 16) {
for (posn = 0; posn < 16; ++posn)
iv[posn] ^= *input++;
blockCipher->encryptBlock(iv, iv);
for (posn = 0; posn < 16; ++posn)
*output++ = iv[posn];
len -= 16;
}
}
void CBCCommon::decrypt(uint8_t *output, const uint8_t *input, size_t len)
{
uint8_t posn;
while (len >= 16) {
blockCipher->decryptBlock(temp, input);
for (posn = 0; posn < 16; ++posn) {
uint8_t in = *input++;
*output++ = temp[posn] ^ iv[posn];
iv[posn] = in;
}
len -= 16;
}
}
void CBCCommon::clear()
{
blockCipher->clear();
clean(iv);
clean(temp);
posn = 16;
}
/**
* \fn void CBCCommon::setBlockCipher(BlockCipher *cipher)
* \brief Sets the block cipher to use for this CBC object.
*
* \param cipher The block cipher to use to implement CBC mode,
* which must have a block size of 16 bytes (128 bits).
*/
/**
* \class CBC CBC.h <CBC.h>
* \brief Implementation of the Cipher Block Chaining (CBC) mode for
* 128-bit block ciphers.
*
* The template parameter T must be a concrete subclass of BlockCipher
* indicating the specific block cipher to use. T must have a block size
* of 16 bytes (128 bits).
*
* For example, the following creates a CBC object using AES192 as the
* underlying cipher:
*
* \code
* CBC<AES192> cbc;
* cbc.setKey(key, 24);
* cbc.setIV(iv, 16);
* cbc.encrypt(output, input, len);
* \endcode
*
* Decryption is similar:
*
* \code
* CBC<AES192> cbc;
* cbc.setKey(key, 24);
* cbc.setIV(iv, 16);
* cbc.decrypt(output, input, len);
* \endcode
*
* The size of the ciphertext will always be the same as the size of
* the plaintext. Also, the length of the plaintext/ciphertext must be a
* multiple of 16. Extra bytes are ignored and not encrypted. The caller
* is responsible for padding the underlying data to a multiple of 16
* using an appropriate padding scheme for the application.
*
* Reference: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
*
* \sa CTR, CFB, OFB
*/
/**
* \fn CBC::CBC()
* \brief Constructs a new CBC object for the block cipher T.
*/

66
src/Cryptography/CBC.h Normal file
View File

@ -0,0 +1,66 @@
/*
* Copyright (C) 2015 Southern Storm Software, Pty Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef CRYPTO_CBC_h
#define CRYPTO_CBC_h
#include "Cipher.h"
#include "BlockCipher.h"
class CBCCommon : public Cipher
{
public:
virtual ~CBCCommon();
size_t keySize() const;
size_t ivSize() const;
bool setKey(const uint8_t *key, size_t len);
bool setIV(const uint8_t *iv, size_t len);
void encrypt(uint8_t *output, const uint8_t *input, size_t len);
void decrypt(uint8_t *output, const uint8_t *input, size_t len);
void clear();
protected:
CBCCommon();
void setBlockCipher(BlockCipher *cipher) { blockCipher = cipher; }
private:
BlockCipher *blockCipher;
uint8_t iv[16];
uint8_t temp[16];
uint8_t posn;
};
template <typename T>
class CBC : public CBCCommon
{
public:
CBC() { setBlockCipher(&cipher); }
private:
T cipher;
};
#endif

View File

@ -1,14 +1,138 @@
#include "Fernet.h" #include "Fernet.h"
#include "HMAC.h"
#include "PKCS7.h"
#include "AES.h"
#include "../Log.h" #include "../Log.h"
#include <stdexcept>
#include <time.h>
using namespace RNS;
using namespace RNS::Cryptography; using namespace RNS::Cryptography;
Fernet::Fernet() { Fernet::Fernet(const Bytes &key) {
log("Fernet object created", LOG_EXTREME);
if (!key) {
throw std::invalid_argument("Fernet key cannot be None");
}
if (key.size() != 32) {
throw std::invalid_argument("Fernet key must be 32 bytes, not " + std::to_string(key.size()));
}
// Python array indexing
// [8:16]
// pos 8 to pos 16
// mid(8, 8)
// [:16]
// start to pos 16 (same as first 16)
// left(16)
// [16:]
// pos 16 to end
// mid(16)
// [-16:]
// last 16
// right(16)
// [:-16]
// all except the last 16
// left(size()-16)
// mid(0, size()-16)
// [-1]
// last element
// [-2]
// seocnd to last element
//self._signing_key = key[:16]
_signing_key = key.left(16);
//self._encryption_key = key[16:]
_encryption_key = key.mid(16);
extreme("Fernet object created");
} }
Fernet::~Fernet() { Fernet::~Fernet() {
log("Fernet object destroyed", LOG_EXTREME); extreme("Fernet object destroyed");
} }
bool Fernet::verify_hmac(const Bytes &token) {
if (token.size() <= 32) {
throw std::invalid_argument("Cannot verify HMAC on token of only " + std::to_string(token.size()) + " bytes");
}
//received_hmac = token[-32:]
Bytes received_hmac = token.right(32);
debug("Fernet::verify_hmac: received_hmac: " + received_hmac.toHex());
//expected_hmac = HMAC.new(self._signing_key, token[:-32]).digest()
Bytes expected_hmac = HMAC::generate(_signing_key, token.left(token.size()-32))->digest();
debug("Fernet::verify_hmac: expected_hmac: " + expected_hmac.toHex());
return (received_hmac == expected_hmac);
}
Bytes Fernet::encrypt(const Bytes &data) {
debug("Fernet::encrypt: plaintext length: " + std::to_string(data.size()));
Bytes iv = random(16);
//time_t current_time = time(nullptr);
extreme("Fernet::encrypt: iv: " + iv.toHex());
extreme("Fernet::encrypt: plaintext: " + data.toHex());
Bytes ciphertext = AES_128_CBC::encrypt(
PKCS7::pad(data),
_encryption_key,
iv
);
debug("Fernet::encrypt: padded ciphertext length: " + std::to_string(ciphertext.size()));
extreme("Fernet::encrypt: ciphertext: " + ciphertext.toHex());
Bytes signed_parts = iv + ciphertext;
//return signed_parts + HMAC::generate(_signing_key, signed_parts)->digest();
Bytes sig(HMAC::generate(_signing_key, signed_parts)->digest());
extreme("Fernet::encrypt: sig: " + sig.toHex());
Bytes token(signed_parts + sig);
debug("Fernet::encrypt: token length: " + std::to_string(token.size()));
return token;
}
Bytes Fernet::decrypt(const Bytes &token) {
debug("Fernet::decrypt: token length: " + std::to_string(token.size()));
if (token.size() < 48) {
throw std::invalid_argument("Cannot decrypt token of only " + std::to_string(token.size()) + " bytes");
}
if (!verify_hmac(token)) {
throw std::invalid_argument("Fernet token HMAC was invalid");
}
//iv = token[:16]
Bytes iv = token.left(16);
extreme("Fernet::decrypt: iv: " + iv.toHex());
//ciphertext = token[16:-32]
Bytes ciphertext = token.mid(16, token.size()-48);
extreme("Fernet::decrypt: ciphertext: " + ciphertext.toHex());
try {
Bytes plaintext = PKCS7::unpad(
AES_128_CBC::decrypt(
ciphertext,
_encryption_key,
iv
)
);
debug("Fernet::encrypt: unpadded plaintext length: " + std::to_string(plaintext.size()));
extreme("Fernet::decrypt: plaintext: " + plaintext.toHex());
debug("Fernet::decrypt: plaintext length: " + std::to_string(plaintext.size()));
return plaintext;
}
catch (std::exception &e) {
warning("Could not decrypt Fernet token");
throw std::runtime_error("Could not decrypt Fernet token");
}
}

View File

@ -1,18 +1,41 @@
#pragma once #pragma once
#include "Random.h"
#include "../Bytes.h"
#include <stdint.h> #include <stdint.h>
namespace RNS { namespace Cryptography { namespace RNS { namespace Cryptography {
/*
This class provides a slightly modified implementation of the Fernet spec
found at: https://github.com/fernet/spec/blob/master/Spec.md
According to the spec, a Fernet token includes a one byte VERSION and
eight byte TIMESTAMP field at the start of each token. These fields are
not relevant to Reticulum. They are therefore stripped from this
implementation, since they incur overhead and leak initiator metadata.
*/
class Fernet { class Fernet {
public: public:
static const uint8_t FERNET_OVERHEAD = 48; // Bytes static const uint8_t FERNET_OVERHEAD = 48; // Bytes
public: public:
Fernet(); static inline Bytes generate_key() { return random(32); }
public:
Fernet(const Bytes &key);
~Fernet(); ~Fernet();
public:
bool verify_hmac(const Bytes &token);
Bytes encrypt(const Bytes &data);
Bytes decrypt(const Bytes &token);
private:
Bytes _signing_key;
Bytes _encryption_key;
}; };
} } } }

28
src/Cryptography/HKDF.cpp Normal file
View File

@ -0,0 +1,28 @@
#include "HKDF.h"
#include <HKDF.h>
#include <SHA256.h>
using namespace RNS;
Bytes RNS::Cryptography::hkdf(size_t length, const Bytes &derive_from, const Bytes &salt /*= Bytes::NONE*/, const Bytes &context /*= Bytes::NONE*/) {
if (length <= 0) {
throw std::invalid_argument("Invalid output key length");
}
if (!derive_from) {
throw std::invalid_argument("Cannot derive key from empty input material");
}
HKDF<SHA256> hkdf;
if (salt) {
hkdf.setKey(derive_from.data(), derive_from.size(), salt.data(), salt.size());
}
else {
hkdf.setKey(derive_from.data(), derive_from.size());
}
Bytes derived;
hkdf.extract(derived.writable(length), length);
return derived;
}

9
src/Cryptography/HKDF.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include "../Bytes.h"
namespace RNS { namespace Cryptography {
Bytes hkdf(size_t length, const Bytes &derive_from, const Bytes &salt = Bytes::NONE, const Bytes &context = Bytes::NONE);
} }

108
src/Cryptography/HMAC.h Normal file
View File

@ -0,0 +1,108 @@
#pragma once
#include "../Bytes.h"
#include <Hash.h>
#include <SHA256.h>
#include <SHA512.h>
#include <stdexcept>
#include <memory>
namespace RNS { namespace Cryptography {
class HMAC {
public:
enum Digest {
DIGEST_NONE,
DIGEST_SHA256,
DIGEST_SHA512,
};
using Ptr = std::shared_ptr<HMAC>;
public:
/*
Create a new HMAC object.
key: bytes or buffer, key for the keyed hash object.
msg: bytes or buffer, Initial input for the hash or None.
digest: The underlying hash algorithm to use
*/
HMAC(const Bytes &key, const Bytes &msg = Bytes::NONE, Digest digest = DIGEST_SHA256) {
if (digest == DIGEST_NONE) {
throw std::invalid_argument("Cannot derive key from empty input material");
}
switch (digest) {
case DIGEST_SHA256:
_hash = std::unique_ptr<Hash>(new SHA256());
break;
case DIGEST_SHA512:
_hash = std::unique_ptr<Hash>(new SHA512());
break;
default:
throw std::invalid_argument("Unknown ior unsuppored digest");
}
_key = key;
_hash->resetHMAC(key.data(), key.size());
if (msg) {
update(msg);
}
}
/*
Feed data from msg into this hashing object.
*/
void update(const Bytes &msg) {
assert(_hash);
_hash->update(msg.data(), msg.size());
}
/*
Return the hash value of this hashing object.
This returns the hmac value as bytes. The object is
not altered in any way by this function; you can continue
updating the object after calling this function.
*/
Bytes digest() {
assert(_hash);
Bytes result;
_hash->finalizeHMAC(_key.data(), _key.size(), result.writable(_hash->hashSize()), _hash->hashSize());
return result;
}
/*
Create a new hashing object and return it.
key: bytes or buffer, The starting key for the hash.
msg: bytes or buffer, Initial input for the hash, or None.
digest: The underlying hash algorithm to use.
You can now feed arbitrary bytes into the object using its update()
method, and can ask for the hash value at any time by calling its digest()
method.
*/
static inline Ptr generate(const Bytes &key, const Bytes &msg = Bytes::NONE, Digest digest = DIGEST_SHA256) {
return Ptr(new HMAC(key, msg, digest));
}
private:
Bytes _key;
std::unique_ptr<Hash> _hash;
};
/*
Fast inline implementation of HMAC.
key: bytes or buffer, The key for the keyed hash object.
msg: bytes or buffer, Input message.
digest: The underlying hash algorithm to use.
*/
inline Bytes digest(const Bytes &key, const Bytes &msg, HMAC::Digest digest = HMAC::DIGEST_SHA256) {
HMAC hmac(key, msg, digest);
hmac.update(msg);
return hmac.digest();
}
} }

View File

@ -1,6 +1,5 @@
#include "Hashes.h" #include "Hashes.h"
#include "../Log.h"
#include "../Bytes.h" #include "../Bytes.h"
#include <SHA256.h> #include <SHA256.h>

61
src/Cryptography/PKCS7.h Normal file
View File

@ -0,0 +1,61 @@
#pragma once
#include "../Bytes.h"
//#include "../Log.h"
#include <stdexcept>
namespace RNS { namespace Cryptography {
class PKCS7 {
public:
static const size_t BLOCKSIZE = 16;
static inline Bytes pad(const Bytes &data, size_t bs = BLOCKSIZE) {
Bytes padded(data);
inplace_pad(padded, bs);
return padded;
}
static inline Bytes unpad(const Bytes &data, size_t bs = BLOCKSIZE) {
Bytes unpadded(data);
inplace_unpad(unpadded, bs);
return unpadded;
}
// updates passed buffer
static inline void inplace_pad(Bytes &data, size_t bs = BLOCKSIZE) {
size_t len = data.size();
//debug("PKCS7::pad: len: " + std::to_string(len));
size_t padlen = bs - (len % bs);
//debug("PKCS7::pad: pad len: " + std::to_string(padlen));
// create byte array of size n?
//v = bytes([padlen])
uint8_t pad[padlen] = {0};
pad[padlen-1] = (uint8_t)padlen;
//return data+v*padlen
data.append(pad, padlen);
//debug("PKCS7::pad: data size: " + std::to_string(data.size()));
}
// updates passed buffer
static inline void inplace_unpad(Bytes &data, size_t bs = BLOCKSIZE) {
size_t len = data.size();
//debug("PKCS7::unpad: len: " + std::to_string(len));
// last byte is pad length
//pad = data[-1]
size_t padlen = (size_t)data.data()[data.size()-1];
//debug("PKCS7::unpad: pad len: " + std::to_string(padlen));
if (padlen > bs) {
throw std::runtime_error("Cannot unpad, invalid padding length of " + std::to_string(padlen) + " bytes");
}
//return data[:len-padlen]
data.resize(len - padlen);
//debug("PKCS7::unpad: data size: " + std::to_string(data.size()));
}
};
} }

16
src/Cryptography/Random.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include "../Bytes.h"
#include <RNG.h>
#include <stdint.h>
namespace RNS { namespace Cryptography {
inline Bytes random(size_t length) {
Bytes rand;
RNG.rand(rand.writable(length), length);
return rand;
}
} }

View File

@ -1,10 +1,12 @@
#pragma once #pragma once
#include "Bytes.h" #include "Bytes.h"
#include "Log.h"
#include <Curve25519.h> #include <Curve25519.h>
#include <memory> #include <memory>
#include <stdexcept>
#include <stdint.h> #include <stdint.h>
namespace RNS { namespace Cryptography { namespace RNS { namespace Cryptography {
@ -167,11 +169,29 @@ namespace RNS { namespace Cryptography {
} }
*/ */
inline Bytes exchange(const Bytes &peer_public_key) { inline Bytes exchange(const Bytes &peer_public_key) {
Bytes sharedKey(peer_public_key); debug("X25519PublicKey::exchange: public key: " + _publicKey.toHex());
Curve25519::dh2(sharedKey.writable(32), _privateKey.writable(32)); debug("X25519PublicKey::exchange: peer public key: " + peer_public_key.toHex());
debug("X25519PublicKey::exchange: pre private key: " + _privateKey.toHex());
Bytes sharedKey;
if (!Curve25519::eval(sharedKey.writable(32), _privateKey.data(), peer_public_key.data())) {
throw std::runtime_error("Peer key is invalid");
}
debug("X25519PublicKey::exchange: shared key: " + sharedKey.toHex());
debug("X25519PublicKey::exchange: post private key: " + _privateKey.toHex());
return sharedKey; return sharedKey;
} }
inline bool verify(const Bytes &peer_public_key) {
debug("X25519PublicKey::exchange: public key: " + _publicKey.toHex());
debug("X25519PublicKey::exchange: peer public key: " + peer_public_key.toHex());
debug("X25519PublicKey::exchange: pre private key: " + _privateKey.toHex());
Bytes sharedKey(peer_public_key);
bool success = Curve25519::dh2(sharedKey.writable(32), _privateKey.writable(32));
debug("X25519PublicKey::exchange: shared key: " + sharedKey.toHex());
debug("X25519PublicKey::exchange: post private key: " + _privateKey.toHex());
return success;
}
private: private:
//Bytes _a; //Bytes _a;
Bytes _privateKey; Bytes _privateKey;

View File

@ -7,7 +7,6 @@
#include <time.h> #include <time.h>
#include <string.h> #include <string.h>
#include <stdexcept>
#include <vector> #include <vector>
using namespace RNS; using namespace RNS;
@ -42,13 +41,12 @@ Destination::Destination(const Identity &identity, const directions direction, c
// Generate the destination address hash // Generate the destination address hash
debug("Destination::Destination: creating hash..."); debug("Destination::Destination: creating hash...");
_object->_hash = hash(_object->_identity, app_name, fullaspects.c_str()); _object->_hash = hash(_object->_identity, app_name, fullaspects.c_str());
debug("Destination::Destination: hash: " + _object->_hash.toHex()); _object->_hexhash = _object->_hash.toHex();
debug("Destination::Destination: hash: " + _object->_hash.toHex());
// CBA TEST CRASH // CBA TEST CRASH
debug("Destination::Destination: creating name hash..."); debug("Destination::Destination: creating name hash...");
_object->_name_hash = Identity::truncated_hash(expand_name(Identity::NONE, app_name, fullaspects.c_str())); _object->_name_hash = Identity::truncated_hash(expand_name(Identity::NONE, app_name, fullaspects.c_str()));
debug("Destination::Destination: name hash: " + _object->_name_hash.toHex()); 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"); debug("Destination::Destination: calling register_destination");
Transport::register_destination(*this); Transport::register_destination(*this);
@ -178,7 +176,7 @@ Packet Destination::announce(const Bytes &app_data, bool path_response, Interfac
} }
else { else {
Bytes destination_hash = _object->_hash; Bytes destination_hash = _object->_hash;
//zBytes random_hash = Identity::get_random_hash()[0:5] << int(time.time()).to_bytes(5, "big"); //random_hash = Identity::get_random_hash()[0:5] << int(time.time()).to_bytes(5, "big")
Bytes random_hash; Bytes random_hash;
Bytes new_app_data(app_data); Bytes new_app_data(app_data);
@ -187,19 +185,19 @@ Packet Destination::announce(const Bytes &app_data, bool path_response, Interfac
} }
Bytes signed_data; Bytes signed_data;
debug("Destination::announce: hash: " + _object->_hash.toHex()); debug("Destination::announce: hash: " + _object->_hash.toHex());
debug("Destination::announce: identity public key: " + _object->_identity.get_public_key().toHex()); debug("Destination::announce: public key: " + _object->_identity.get_public_key().toHex());
debug("Destination::announce: name hash: " + _object->_name_hash.toHex()); debug("Destination::announce: name hash: " + _object->_name_hash.toHex());
debug("Destination::announce: random hash: " + random_hash.toHex()); debug("Destination::announce: random hash: " + random_hash.toHex());
debug("Destination::announce: new app data: " + new_app_data.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; signed_data << _object->_hash << _object->_identity.get_public_key() << _object->_name_hash << random_hash;
if (new_app_data) { if (new_app_data) {
signed_data << new_app_data; signed_data << new_app_data;
} }
debug("Destination::announce: signed data: " + signed_data.toHex()); debug("Destination::announce: signed data: " + signed_data.toHex());
Bytes signature(_object->_identity.sign(signed_data)); Bytes signature(_object->_identity.sign(signed_data));
debug("Destination::announce: signature: " + signature.toHex()); debug("Destination::announce: signature: " + signature.toHex());
announce_data << _object->_identity.get_public_key() << _object->_name_hash << random_hash << signature; announce_data << _object->_identity.get_public_key() << _object->_name_hash << random_hash << signature;
@ -209,7 +207,7 @@ Packet Destination::announce(const Bytes &app_data, bool path_response, Interfac
_object->_path_responses.insert({tag, {time(nullptr), announce_data}}); _object->_path_responses.insert({tag, {time(nullptr), announce_data}});
} }
debug("Destination::announce: announce_data: " + announce_data.toHex()); debug("Destination::announce: announce_data:" + announce_data.toHex());
Packet::context_types announce_context = Packet::CONTEXT_NONE; Packet::context_types announce_context = Packet::CONTEXT_NONE;
if (path_response) { if (path_response) {
@ -217,49 +215,99 @@ Packet Destination::announce(const Bytes &app_data, bool path_response, Interfac
} }
debug("Destination::announce: creating announce packet..."); debug("Destination::announce: creating announce packet...");
Packet announce_packet(*this, announce_data, Packet::ANNOUNCE, announce_context,Transport::BROADCAST, Packet::HEADER_1, nullptr, attached_interface); //announce_packet = RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE, context = announce_context, attached_interface = attached_interface)
//Packet announce_packet(*this, announce_data, Packet::ANNOUNCE, announce_context, Transport::BROADCAST, Packet::HEADER_1, nullptr, attached_interface);
Packet announce_packet(*this, announce_data, Packet::DATA, announce_context, Transport::BROADCAST, Packet::HEADER_1, nullptr, attached_interface);
extreme("Destination::announce: pre announce packet: " + announce_packet.toString());
if (send) { if (send) {
announce_packet.send(); announce_packet.send();
return Packet::NONE; extreme("Destination::announce: post announce packet: " + announce_packet.toString());
// CBA temporarily returning copy of sent packet for testing purposes
//return Packet::NONE;
return announce_packet;
} }
else { else {
return announce_packet; 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. /*
Registers a request handler.
:param path: The path for the request handler to be registered.
:param response_generator: A function or method with the signature *response_generator(path, data, request_id, link_id, remote_identity, requested_at)* to be called. Whatever this funcion returns will be sent as a response to the requester. If the function returns ``None``, no response will be sent.
:param allow: One of ``RNS.Destination.ALLOW_NONE``, ``RNS.Destination.ALLOW_ALL`` or ``RNS.Destination.ALLOW_LIST``. If ``RNS.Destination.ALLOW_LIST`` is set, the request handler will only respond to requests for identified peers in the supplied list.
:param allowed_list: A list of *bytes-like* :ref:`RNS.Identity<api-identity>` hashes.
:raises: ``ValueError`` if any of the supplied arguments are invalid.
*/ */
void Destination::set_link_established_callback(Callbacks::link_established callback) { /*
void Destination::register_request_handler(const Bytes &path, response_generator = None, request_policies allow = ALLOW_NONE, allowed_list = None) {
if path == None or path == "":
raise ValueError("Invalid path specified")
elif not callable(response_generator):
raise ValueError("Invalid response generator specified")
elif not allow in Destination.request_policies:
raise ValueError("Invalid request policy")
else:
path_hash = RNS.Identity.truncated_hash(path.encode("utf-8"))
request_handler = [path, response_generator, allow, allowed_list]
self.request_handlers[path_hash] = request_handler
}
*/
/*
Deregisters a request handler.
:param path: The path for the request handler to be deregistered.
:returns: True if the handler was deregistered, otherwise False.
*/
/*
bool Destination::deregister_request_handler(const Bytes &path) {
path_hash = RNS.Identity.truncated_hash(path.encode("utf-8"))
if path_hash in self.request_handlers:
self.request_handlers.pop(path_hash)
return True
else:
return False
}
*/
void Destination::receive(const Packet &packet) {
assert(_object); assert(_object);
_object->_callbacks._link_established = callback; if (packet._packet_type == Packet::LINKREQUEST) {
Bytes plaintext(packet._data);
incoming_link_request(plaintext, packet);
}
else {
// CBA TEST determine why packet._data is being used instead of packet._raw for incoming packets
//Bytes plaintext(decrypt(packet._data));
Bytes plaintext(decrypt(packet._raw.mid(19)));
extreme("Destination::receive: decrypted data: " + plaintext.toHex());
if (plaintext) {
if (packet._packet_type == RNS::Packet::DATA) {
if (_object->_callbacks._packet) {
try {
_object->_callbacks._packet(plaintext, packet);
}
catch (std::exception &e) {
debug("Error while executing receive callback from " + toString() + ". The contained exception was: " + e.what());
}
}
}
}
}
} }
/* void Destination::incoming_link_request(const Bytes &data, const Packet &packet) {
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); assert(_object);
_object->_callbacks._packet = callback; if (_object->_accept_link_requests) {
} //zlink = RNS::Link::validate_request(data, packet);
//zif (link) {
/* //z _links.append(link);
Registers a function to be called when a proof has been requested for //z}
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;
} }
/* /*
@ -270,7 +318,7 @@ Encrypts information for ``RNS.Destination.SINGLE`` or ``RNS.Destination.GROUP``
*/ */
Bytes Destination::encrypt(const Bytes &data) { Bytes Destination::encrypt(const Bytes &data) {
assert(_object); assert(_object);
debug("Destination::encrypt: encrypting bytes"); debug("Destination::encrypt: encrypting data...");
if (_object->_type == Destination::PLAIN) { if (_object->_type == Destination::PLAIN) {
return data; return data;
@ -304,7 +352,7 @@ Decrypts information for ``RNS.Destination.SINGLE`` or ``RNS.Destination.GROUP``
*/ */
Bytes Destination::decrypt(const Bytes &data) { Bytes Destination::decrypt(const Bytes &data) {
assert(_object); assert(_object);
debug("Destination::decrypt: decrypting bytes"); debug("Destination::decrypt: decrypting data...");
if (_object->_type == Destination::PLAIN) { if (_object->_type == Destination::PLAIN) {
return data; return data;

View File

@ -4,12 +4,13 @@
#include "Identity.h" #include "Identity.h"
#include "Bytes.h" #include "Bytes.h"
#include <stdint.h>
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <map> #include <map>
#include <stdexcept>
#include <stdint.h>
namespace RNS { namespace RNS {
@ -36,14 +37,15 @@ namespace RNS {
public: public:
class Callbacks { class Callbacks {
public: public:
using link_established = void(*)(Link *link); using link_established = void(*)(const Link &link);
//using packet = void(*)(uint8_t *data, uint16_t data_len, Packet *packet); //using packet = void(*)(uint8_t *data, uint16_t data_len, Packet *packet);
using packet = void(*)(const Bytes &data, Packet *packet); using packet = void(*)(const Bytes &data, const Packet &packet);
using proof_requested = void(*)(Packet *packet); using proof_requested = void(*)(const Packet &packet);
public:
link_established _link_established = nullptr; link_established _link_established = nullptr;
packet _packet = nullptr; packet _packet = nullptr;
proof_requested _proof_requested = nullptr; proof_requested _proof_requested = nullptr;
friend class Detination;
}; };
//typedef std::pair<time_t, std::string> Response; //typedef std::pair<time_t, std::string> Response;
@ -106,11 +108,64 @@ namespace RNS {
public: public:
Packet announce(const Bytes &app_data = {}, bool path_response = false, Interface *attached_interface = nullptr, const Bytes &tag = {}, bool send = true); Packet announce(const Bytes &app_data = {}, bool path_response = false, Interface *attached_interface = nullptr, const Bytes &tag = {}, bool send = true);
/*
Set or query whether the destination accepts incoming link requests.
:param accepts: If ``True`` or ``False``, this method sets whether the destination accepts incoming link requests. If not provided or ``None``, the method returns whether the destination currently accepts link requests.
:returns: ``True`` or ``False`` depending on whether the destination accepts incoming link requests, if the *accepts* parameter is not provided or ``None``.
*/
inline void set_accepts_links(bool accepts) { assert(_object); _object->_accept_link_requests = accepts; } 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; } 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); 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.
*/
inline void 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.
*/
inline void 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.
*/
inline void set_proof_requested_callback(Callbacks::proof_requested callback) {
assert(_object);
_object->_callbacks._proof_requested = callback;
}
/*
Sets the destinations proof strategy.
:param proof_strategy: One of ``RNS.Destination.PROVE_NONE``, ``RNS.Destination.PROVE_ALL`` or ``RNS.Destination.PROVE_APP``. If ``RNS.Destination.PROVE_APP`` is set, the `proof_requested_callback` will be called to determine whether a proof should be sent or not.
*/
inline void set_proof_strategy(proof_strategies proof_strategy) {
assert(_object);
//if (proof_strategy <= PROOF_NONE) {
// throw throw std::invalid_argument("Unsupported proof strategy");
//}
_object->_proof_strategy = proof_strategy;
}
void receive(const Packet &packet);
void incoming_link_request(const Bytes &data, const Packet &packet);
Bytes encrypt(const Bytes &data); Bytes encrypt(const Bytes &data);
Bytes decrypt(const Bytes &data); Bytes decrypt(const Bytes &data);
Bytes sign(const Bytes &message); Bytes sign(const Bytes &message);
@ -124,6 +179,8 @@ namespace RNS {
inline uint16_t mtu() const { assert(_object); return _object->_mtu; } inline uint16_t mtu() const { assert(_object); return _object->_mtu; }
inline void mtu(uint16_t mtu) { assert(_object); _object->_mtu = mtu; } inline void mtu(uint16_t mtu) { assert(_object); _object->_mtu = mtu; }
inline std::string toString() const { assert(_object); return "{Destination:" + _object->_hash.toHex() + "}"; }
private: private:
class Object { class Object {
public: public:

View File

@ -3,8 +3,10 @@
#include "Reticulum.h" #include "Reticulum.h"
#include "Packet.h" #include "Packet.h"
#include "Log.h" #include "Log.h"
#include "Cryptography/Hashes.h"
#include "Cryptography/X25519.h" #include "Cryptography/X25519.h"
#include "Cryptography/HKDF.h"
#include "Cryptography/Fernet.h"
#include "Cryptography/Random.h"
#include <string.h> #include <string.h>
@ -21,18 +23,22 @@ Identity::Identity(bool create_keys) : _object(new Object()) {
void Identity::createKeys() { void Identity::createKeys() {
assert(_object); assert(_object);
// CRYPTO: create encryption private keys
_object->_prv = Cryptography::X25519PrivateKey::generate(); _object->_prv = Cryptography::X25519PrivateKey::generate();
_object->_prv_bytes = _object->_prv->private_bytes(); _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(); // CRYPTO: create encryption public keys
_object->_sig_prv_bytes = _object->_sig_prv->private_bytes();
debug("Identity::createKeys: sig prv bytes: " + _object->_sig_prv_bytes.toHex());
_object->_pub = _object->_prv->public_key(); _object->_pub = _object->_prv->public_key();
_object->_pub_bytes = _object->_pub->public_bytes(); _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());
// CRYPTO: create signature private keys
_object->_sig_prv = Cryptography::Ed25519PrivateKey::generate();
_object->_sig_prv_bytes = _object->_sig_prv->private_bytes();
debug("Identity::createKeys: sig prv bytes: " + _object->_sig_prv_bytes.toHex());
// CRYPTO: create signature public keys
_object->_sig_pub = _object->_sig_prv->public_key(); _object->_sig_pub = _object->_sig_prv->public_key();
_object->_sig_pub_bytes = _object->_sig_pub->public_bytes(); _object->_sig_pub_bytes = _object->_sig_pub->public_bytes();
debug("Identity::createKeys: sig pub bytes: " + _object->_sig_pub_bytes.toHex()); debug("Identity::createKeys: sig pub bytes: " + _object->_sig_pub_bytes.toHex());
@ -42,43 +48,6 @@ void Identity::createKeys() {
verbose("Identity keys created for " + _object->_hash.toHex()); verbose("Identity keys created for " + _object->_hash.toHex());
} }
/*
:returns: The public key as *bytes*
*/
Bytes Identity::get_public_key() {
assert(_object);
return _object->_pub_bytes + _object->_sig_pub_bytes;
}
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);
};
/*
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. Encrypts information for the identity.
@ -89,31 +58,34 @@ Encrypts information for the identity.
*/ */
Bytes Identity::encrypt(const Bytes &plaintext) { Bytes Identity::encrypt(const Bytes &plaintext) {
assert(_object); assert(_object);
if (_object->_pub) { debug("Identity::encrypt: encrypting data...");
Cryptography::X25519PrivateKey::Ptr ephemeral_key = Cryptography::X25519PrivateKey::generate(); if (!_object->_pub) {
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"); throw std::runtime_error("Encryption failed because identity does not hold a public key");
} }
Cryptography::X25519PrivateKey::Ptr ephemeral_key = Cryptography::X25519PrivateKey::generate();
Bytes ephemeral_pub_bytes = ephemeral_key->public_key()->public_bytes();
debug("Identity::encrypt: ephemeral public key: " + ephemeral_pub_bytes.toHex());
// CRYPTO: create shared key for key exchange using own public key
//shared_key = ephemeral_key.exchange(self.pub)
Bytes shared_key = ephemeral_key->exchange(_object->_pub_bytes);
debug("Identity::encrypt: shared key: " + shared_key.toHex());
Bytes derived_key = Cryptography::hkdf(
32,
shared_key,
get_salt(),
get_context()
);
debug("Identity::encrypt: derived key: " + derived_key.toHex());
Cryptography::Fernet fernet(derived_key);
debug("Identity::encrypt: Fernet encrypting data of length " + std::to_string(plaintext.size()));
extreme("Identity::encrypt: plaintext: " + plaintext.toHex());
Bytes ciphertext = fernet.encrypt(plaintext);
extreme("Identity::encrypt: ciphertext: " + ciphertext.toHex());
return ephemeral_pub_bytes + ciphertext;
} }
@ -126,42 +98,49 @@ Decrypts information for the identity.
*/ */
Bytes Identity::decrypt(const Bytes &ciphertext_token) { Bytes Identity::decrypt(const Bytes &ciphertext_token) {
assert(_object); assert(_object);
if (_object->_prv) { debug("Identity::decrypt: decrypting data...");
if (ciphertext_token.size() > Identity::KEYSIZE/8/2) { if (!_object->_prv) {
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"); throw std::runtime_error("Decryption failed because identity does not hold a private key");
} }
if (ciphertext_token.size() <= Identity::KEYSIZE/8/2) {
debug("Decryption failed because the token size " + std::to_string(ciphertext_token.size()) + " was invalid.");
return Bytes::NONE;
}
Bytes plaintext;
try {
//peer_pub_bytes = ciphertext_token[:Identity.KEYSIZE//8//2]
Bytes peer_pub_bytes = ciphertext_token.left(Identity::KEYSIZE/8/2);
//peer_pub = X25519PublicKey.from_public_bytes(peer_pub_bytes)
//Cryptography::X25519PublicKey::Ptr peer_pub = Cryptography::X25519PublicKey::from_public_bytes(peer_pub_bytes);
debug("Identity::decrypt: peer public key: " + peer_pub_bytes.toHex());
// CRYPTO: create shared key for key exchange using peer public key
//shared_key = _object->_prv->exchange(peer_pub);
Bytes shared_key = _object->_prv->exchange(peer_pub_bytes);
debug("Identity::decrypt: shared key: " + shared_key.toHex());
Bytes derived_key = Cryptography::hkdf(
32,
shared_key,
get_salt(),
get_context()
);
debug("Identity::decrypt: derived key: " + derived_key.toHex());
Cryptography::Fernet fernet(derived_key);
//ciphertext = ciphertext_token[Identity.KEYSIZE//8//2:]
Bytes ciphertext(ciphertext_token.mid(Identity::KEYSIZE/8/2));
debug("Identity::decrypt: Fernet decrypting data of length " + std::to_string(ciphertext.size()));
extreme("Identity::decrypt: ciphertext: " + ciphertext.toHex());
plaintext = fernet.decrypt(ciphertext);
extreme("Identity::decrypt: plaintext: " + plaintext.toHex());
debug("Identity::decrypt: Fernet decrypted data of length " + std::to_string(plaintext.size()));
}
catch (std::exception &e) {
debug("Decryption by " + toString() + " failed: " + e.what());
}
return plaintext;
} }
/* /*
@ -173,18 +152,16 @@ Signs information by the identity.
*/ */
Bytes Identity::sign(const Bytes &message) { Bytes Identity::sign(const Bytes &message) {
assert(_object); assert(_object);
if (_object->_sig_prv) { if (!_object->_sig_prv) {
try {
return _object->_sig_prv->sign(message);
}
catch (std::exception &e) {
error("The identity " + toString() + " could not sign the requested message. The contained exception was: " + e.what());
throw e;
}
}
else {
throw std::runtime_error("Signing failed because identity does not hold a private key"); throw std::runtime_error("Signing failed because identity does not hold a private key");
} }
try {
return _object->_sig_prv->sign(message);
}
catch (std::exception &e) {
error("The identity " + toString() + " could not sign the requested message. The contained exception was: " + e.what());
throw e;
}
} }
/* /*

View File

@ -5,9 +5,10 @@
//#include "Destination.h" //#include "Destination.h"
#include "Log.h" #include "Log.h"
#include "Bytes.h" #include "Bytes.h"
#include "Cryptography/Fernet.h" #include "Cryptography/Hashes.h"
#include "Cryptography/X25519.h"
#include "Cryptography/Ed25519.h" #include "Cryptography/Ed25519.h"
#include "Cryptography/X25519.h"
#include "Cryptography/Fernet.h"
#include <memory> #include <memory>
@ -66,11 +67,51 @@ namespace RNS {
public: public:
void createKeys(); void createKeys();
Bytes get_public_key(); /*
void update_hashes(); :returns: The public key as *bytes*
*/
inline Bytes get_public_key() {
assert(_object);
return _object->_pub_bytes + _object->_sig_pub_bytes;
}
inline void update_hashes() {
assert(_object);
_object->_hash = truncated_hash(get_public_key());
debug("Identity::update_hashes: hash: " + _object->_hash.toHex());
_object->_hexhash = _object->_hash.toHex();
};
static Bytes full_hash(const Bytes &data); /*
static Bytes truncated_hash(const Bytes &data); Get a SHA-256 hash of passed data.
:param data: Data to be hashed as *bytes*.
:returns: SHA-256 hash as *bytes*
*/
static inline Bytes 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 inline Bytes truncated_hash(const Bytes &data) {
//return Identity.full_hash(data)[:(Identity.TRUNCATED_HASHLENGTH//8)]
return full_hash(data).left(TRUNCATED_HASHLENGTH/8);
}
/*
Get a random SHA-256 hash.
:param data: Data to be hashed as *bytes*.
:returns: Truncated SHA-256 hash of random data as *bytes*
*/
static inline Bytes get_random_hash() {
return truncated_hash(Cryptography::random(Identity::TRUNCATED_HASHLENGTH/8));
}
inline Bytes get_salt() { assert(_object); return _object->_hash; }
inline Bytes get_context() { return Bytes::NONE; }
Bytes encrypt(const Bytes &plaintext); Bytes encrypt(const Bytes &plaintext);
Bytes decrypt(const Bytes &ciphertext_token); Bytes decrypt(const Bytes &ciphertext_token);
@ -87,7 +128,7 @@ namespace RNS {
inline Bytes hash() const { assert(_object); return _object->_hash; } inline Bytes hash() const { assert(_object); return _object->_hash; }
inline std::string hexhash() const { assert(_object); return _object->_hexhash; } inline std::string hexhash() const { assert(_object); return _object->_hexhash; }
inline std::string toString() const { assert(_object); return _object->_hash.toHex(); } inline std::string toString() const { assert(_object); return "{Identity:" + _object->_hash.toHex() + "}"; }
private: private:
class Object { class Object {

View File

@ -45,6 +45,7 @@ void RNS::doLog(const char* msg, LogLevel level) {
Serial.print(getLevelName(level)); Serial.print(getLevelName(level));
Serial.print(" "); Serial.print(" ");
Serial.println(msg); Serial.println(msg);
Serial.flush();
#else #else
printf("%s: %s\n", getLevelName(level), msg); printf("%s: %s\n", getLevelName(level), msg);
#endif #endif

View File

@ -8,10 +8,10 @@
using namespace RNS; 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)) { 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 Bytes &transport_id /*= Bytes::NONE*/, bool create_receipt /*= true*/) : _object(new Object(destination, attached_interface)) {
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) { if (_object->_destination) {
extreme("Creating packet with detination...");
// CBA TODO handle NONE // CBA TODO handle NONE
if (transport_type == -1) { if (transport_type == -1) {
transport_type = Transport::BROADCAST; transport_type = Transport::BROADCAST;
@ -23,31 +23,37 @@ Packet::Packet(const Destination &destination, const Interface &attached_interfa
_transport_type = transport_type; _transport_type = transport_type;
_context = context; _context = context;
//transport_id = transport_id; _transport_id = transport_id;
//setTransportId(transport_id);
if (transport_id != nullptr) {
memcpy(_transport_id, transport_id, Reticulum::DESTINATION_LENGTH);
}
//data = data; _data = data;
//setData(data); if (_data.size() > MDU) {
_truncated = true;
_data.resize(MDU);
}
/*
if (data) { if (data) {
// data is plaintext
if (data.size() > MDU) { if (data.size() > MDU) {
_truncated = true; _truncated = true;
// CBA TODO add method to truncate // CBA TODO add method to truncate
//zdata_len = MDU; //zdata_len = MDU;
} }
_data = _raw + Reticulum::HEADER_MAXSIZE;
memcpy(_data, data.data(), data.size()); memcpy(_data, data.data(), data.size());
} }
*/
_flags = get_packed_flags(); _flags = get_packed_flags();
_create_receipt = create_receipt; _create_receipt = create_receipt;
} }
else { else {
//_raw = data; extreme("Creating packet without detination...");
_raw = data;
/*
if (data) { if (data) {
memcpy(_raw, data.data(), data.size()); memcpy(_raw, data.data(), data.size());
} }
*/
_packed = true; _packed = true;
_fromPacked = true; _fromPacked = true;
_create_receipt = false; _create_receipt = false;
@ -60,57 +66,6 @@ Packet::~Packet() {
} }
/*
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 Packet::get_packed_flags() {
uint8_t packed_flags = 0; uint8_t packed_flags = 0;
if (_context == LRPROOF) { if (_context == LRPROOF) {
@ -122,6 +77,13 @@ uint8_t Packet::get_packed_flags() {
return packed_flags; return packed_flags;
} }
void Packet::unpack_flags(uint8_t flags) {
_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);
}
/* /*
== Reticulum Wire Format ====== == Reticulum Wire Format ======
@ -287,20 +249,22 @@ but excluding any interface access codes.
// | ...destination_2 | context | data ... | // | ...destination_2 | context | data ... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/*
void Packet::pack() { void Packet::pack() {
assert(_object); assert(_object);
debug("Packet::pack: packing packet..."); debug("Packet::pack: packing packet...");
extreme("Packet::pack: pre hops: " + std::to_string(_hops));
//memcpy(_destination_hash, _destination->_hash.data(), Reticulum::DESTINATION_LENGTH); //memcpy(_destination_hash, _destination->_hash.data(), Reticulum::DESTINATION_LENGTH);
memcpy(_destination_hash, _object->_destination.hash().data(), _object->_destination.hash().size()); memcpy(_destination_hash, _object->_destination.hash().data(), _object->_destination.hash().size());
_header[0] = _flags;
_header[1] = _hops;
//uint8_t *ciphertext; //uint8_t *ciphertext;
if (_context == LRPROOF) { if (_context == LRPROOF) {
// write header // write header
//memcpy(_header+2, _destination->_link_id, Reticulum::DESTINATION_LENGTH); _header = _data - Reticulum::HEADER_MINSIZE;
_header[0] = _flags;
_header[1] = _hops;
//memcpy(header+2, _destination->_link_id, Reticulum::DESTINATION_LENGTH);
debug("Packet::pack: destination link id: " + _object->_destination.link_id().toHex() ); debug("Packet::pack: destination link id: " + _object->_destination.link_id().toHex() );
memcpy(_header+2, _object->_destination.link_id().data(), _object->_destination.link_id().size()); memcpy(_header+2, _object->_destination.link_id().data(), _object->_destination.link_id().size());
_header[Reticulum::DESTINATION_LENGTH+2] = _context; _header[Reticulum::DESTINATION_LENGTH+2] = _context;
@ -311,7 +275,10 @@ void Packet::pack() {
else { else {
if (_header_type == HEADER_1) { if (_header_type == HEADER_1) {
// write header // write header
//memcpy(_header+2, _destination->_hash.data(), Reticulum::DESTINATION_LENGTH); _header = _data - Reticulum::HEADER_MINSIZE;
_header[0] = _flags;
_header[1] = _hops;
//memcpy(header+2, _destination->_hash.data(), Reticulum::DESTINATION_LENGTH);
debug("Packet::pack: destination hash: " + _object->_destination.hash().toHex() ); debug("Packet::pack: destination hash: " + _object->_destination.hash().toHex() );
memcpy(_header+2, _object->_destination.hash().data(), _object->_destination.hash().size()); memcpy(_header+2, _object->_destination.hash().data(), _object->_destination.hash().size());
_header[Reticulum::DESTINATION_LENGTH+2] = _context; _header[Reticulum::DESTINATION_LENGTH+2] = _context;
@ -359,8 +326,9 @@ void Packet::pack() {
//memcpy(data, _data, _data_len); //memcpy(data, _data, _data_len);
//_data_len = _destination->encrypt(_data, data, _data_len); //_data_len = _destination->encrypt(_data, data, _data_len);
Bytes plaintext(_data, _data_len); Bytes plaintext(_data, _data_len);
Bytes bytes = _object->_destination.encrypt(plaintext); Bytes ciphertext = _object->_destination.encrypt(plaintext);
_data_len = bytes.size(); memcpy(_data, ciphertext.data(), ciphertext.size());
_data_len = ciphertext.size();
} }
} }
else if (_header_type == HEADER_2) { else if (_header_type == HEADER_2) {
@ -368,8 +336,11 @@ void Packet::pack() {
throw std::invalid_argument("Packet with header type 2 must have a transport ID"); throw std::invalid_argument("Packet with header type 2 must have a transport ID");
} }
// write header // write header
_header = _data - Reticulum::HEADER_MAXSIZE;
_header[0] = _flags;
_header[1] = _hops;
memcpy(_header+2, _transport_id, Reticulum::DESTINATION_LENGTH); memcpy(_header+2, _transport_id, Reticulum::DESTINATION_LENGTH);
//memcpy(_header+Reticulum::DESTINATION_LENGTH+2, _destination->_hash.data(), Reticulum::DESTINATION_LENGTH); //memcpy(header+Reticulum::DESTINATION_LENGTH+2, _destination->_hash.data(), Reticulum::DESTINATION_LENGTH);
debug("Packet::pack: destination hash: " + _object->_destination.hash().toHex() ); debug("Packet::pack: destination hash: " + _object->_destination.hash().toHex() );
memcpy(_header+Reticulum::DESTINATION_LENGTH+2, _object->_destination.hash().data(), _object->_destination.hash().size()); memcpy(_header+Reticulum::DESTINATION_LENGTH+2, _object->_destination.hash().data(), _object->_destination.hash().size());
_header[2*Reticulum::DESTINATION_LENGTH+2] = _context; _header[2*Reticulum::DESTINATION_LENGTH+2] = _context;
@ -390,12 +361,104 @@ void Packet::pack() {
_packed = true; _packed = true;
update_hash(); update_hash();
extreme("Packet::pack: post hops: " + std::to_string(_hops));
}
*/
void Packet::pack() {
assert(_object);
debug("Packet::pack: packing packet...");
extreme("Packet::pack: pre hops: " + std::to_string(_hops));
_destination_hash = _object->_destination.hash();
_raw.clear();
_raw << _flags;
_raw << _hops;
if (_context == LRPROOF) {
debug("Packet::pack: destination link id: " + _object->_destination.link_id().toHex() );
_raw << _object->_destination.link_id();
_raw << (uint8_t)_context;
_raw << _data;
}
else {
if (_header_type == HEADER_1) {
debug("Packet::pack: destination hash: " + _object->_destination.hash().toHex() );
_raw << _object->_destination.hash();
_raw << (uint8_t)_context;
if (_packet_type == ANNOUNCE) {
// Announce packets are not encrypted
_raw << _data;
}
else if (_packet_type == LINKREQUEST) {
// Link request packets are not encrypted
_raw << _data;
}
else if (_packet_type == PROOF && _context == RESOURCE_PRF) {
// Resource proofs are not encrypted
_raw << _data;
}
else if (_packet_type == PROOF && _object->_destination.type() == Destination::LINK) {
// Packet proofs over links are not encrypted
_raw << _data;
}
else if (_context == RESOURCE) {
// A resource takes care of encryption
// by itself
_raw << _data;
}
else if (_context == KEEPALIVE) {
// Keepalive packets contain no actual
// data
_raw << _data;
}
else if (_context == CACHE_REQUEST) {
// Cache-requests are not encrypted
_raw << _data;
}
else {
// In all other cases, we encrypt the packet
// with the destination's encryption method
_raw << _object->_destination.encrypt(_data);
}
}
else if (_header_type == HEADER_2) {
if (!_transport_id) {
throw std::invalid_argument("Packet with header type 2 must have a transport ID");
}
debug("Packet::pack: transport id: " + _transport_id.toHex() );
debug("Packet::pack: destination hash: " + _object->_destination.hash().toHex() );
_raw << _transport_id;
_raw << _object->_destination.hash();
_raw << (uint8_t)_context;
if (_packet_type == ANNOUNCE) {
// Announce packets are not encrypted
_raw << _data;
}
}
}
if (_raw.size() > _mtu) {
throw std::length_error("Packet size of " + std::to_string(_raw.size()) + " exceeds MTU of " + std::to_string(_mtu) +" bytes");
}
_packed = true;
update_hash();
extreme("Packet::pack: post hops: " + std::to_string(_hops));
} }
/*
bool Packet::unpack() { bool Packet::unpack() {
assert(_object); assert(_object);
debug("Packet::unpack: unpacking packet..."); debug("Packet::unpack: unpacking packet...");
extreme("Packet::unpack: pre hops: " + std::to_string(_hops));
try { try {
// read header
_flags = _raw[0]; _flags = _raw[0];
_hops = _raw[1]; _hops = _raw[1];
@ -431,6 +494,58 @@ bool Packet::unpack() {
return false; return false;
} }
extreme("Packet::unpack: post hops: " + std::to_string(_hops));
return true;
}
*/
bool Packet::unpack() {
assert(_object);
debug("Packet::unpack: unpacking packet...");
extreme("Packet::unpack: pre hops: " + std::to_string(_hops));
try {
if (_raw.size() < Reticulum::HEADER_MINSIZE) {
throw std::length_error("Packet size of " + std::to_string(_raw.size()) + " does not meet minimum header size of " + std::to_string(Reticulum::HEADER_MINSIZE) +" bytes");
}
const uint8_t *raw = _raw.data();
// read header
_flags = raw[0];
_hops = raw[1];
unpack_flags(_flags);
// CBA TODO detect invalid flags and throw error
if (false) {
log("Received malformed packet, dropping it.");
return false;
}
if (_header_type == HEADER_2) {
if (_raw.size() < Reticulum::HEADER_MAXSIZE) {
throw std::length_error("Packet size of " + std::to_string(_raw.size()) + " does not meet minimum header size of " + std::to_string(Reticulum::HEADER_MAXSIZE) +" bytes");
}
_transport_id.assign(raw+2, Reticulum::DESTINATION_LENGTH);
_destination_hash.assign(raw+Reticulum::DESTINATION_LENGTH+2, Reticulum::DESTINATION_LENGTH);
_context = static_cast<context_types>(raw[2*Reticulum::DESTINATION_LENGTH+2]);
_data.assign(raw+2*Reticulum::DESTINATION_LENGTH+3, _raw.size()-(2*Reticulum::DESTINATION_LENGTH+3));
}
else {
_transport_id.clear();
_destination_hash.assign(raw+2, Reticulum::DESTINATION_LENGTH);
_context = static_cast<context_types>(raw[Reticulum::DESTINATION_LENGTH+2]);
_data.assign(raw+Reticulum::DESTINATION_LENGTH+3, _raw.size()-(Reticulum::DESTINATION_LENGTH+3));
}
_packed = false;
update_hash();
}
catch (std::exception& e) {
error(std::string("Received malformed packet, dropping it. The contained exception was: ") + e.what());
return false;
}
extreme("Packet::unpack: post hops: " + std::to_string(_hops));
return true; return true;
} }
@ -525,8 +640,15 @@ Bytes Packet::getTruncatedHash() {
Bytes Packet::get_hashable_part() { Bytes Packet::get_hashable_part() {
assert(_object); assert(_object);
Bytes hashable_part; Bytes hashable_part;
hashable_part << (uint8_t)(_raw[0] & 0b00001111); hashable_part << (uint8_t)(_raw.data()[0] & 0b00001111);
hashable_part.append(_data-Reticulum::DESTINATION_LENGTH-1, _data_len+Reticulum::DESTINATION_LENGTH+1); if (_header_type == Packet::HEADER_2) {
//hashable_part += self.raw[(RNS.Identity.TRUNCATED_HASHLENGTH//8)+2:]
hashable_part << _raw.mid((Identity::TRUNCATED_HASHLENGTH/8)+2);
}
else {
//hashable_part += self.raw[2:];
hashable_part << _raw.mid(2);
}
return hashable_part; return hashable_part;
} }
@ -536,3 +658,41 @@ Bytes Packet::get_hashable_part() {
// return ProofDestination(); // return ProofDestination();
//} //}
std::string Packet::toString() {
if (_packed) {
//unpack();
}
std::string dump;
dump = "\n--------------------\n";
dump += "flags: " + hexFromByte(_flags) + "\n";
dump += " header_type: " + std::to_string(_header_type) + "\n";
dump += " transport_type: " + std::to_string(_transport_type) + "\n";
dump += " destination_type: " + std::to_string(_destination_type) + "\n";
dump += " packet_type: " + std::to_string(_packet_type) + "\n";
dump += "hops: " + std::to_string(_hops) + "\n";
dump += "transport: " + _transport_id.toHex() + "\n";
dump += "destination: " + _destination_hash.toHex() + "\n";
dump += "context_type: " + std::to_string(_header_type) + "\n";
dump += "plaintext: " + _data.toHex() + "\n";
dump += " length: " + std::to_string(_data.size()) + "\n";
dump += "raw: " + _raw.toHex() + "\n";
dump += " length: " + std::to_string(_raw.size()) + "\n";
if (_raw.size() > 0) {
size_t header_len = Reticulum::HEADER_MINSIZE;
if (_header_type == HEADER_2) {
header_len = Reticulum::HEADER_MAXSIZE;
}
dump += " header: " + _raw.left(header_len).toHex() + "\n";
dump += " key: " + _raw.mid(header_len, Identity::KEYSIZE/8/2).toHex() + "\n";
Bytes ciphertext(_raw.mid(header_len+Identity::KEYSIZE/8/2));
dump += " ciphertext: " + ciphertext.toHex() + "\n";
dump += " length: " + std::to_string(ciphertext.size()) + "\n";
dump += " iv: " + ciphertext.left(16).toHex() + "\n";
dump += " sig: " + ciphertext.right(32).toHex() + "\n";
dump += " aes ciphertext: " + ciphertext.mid(16, ciphertext.size()-48).toHex() + "\n";
dump += " length: " + std::to_string(ciphertext.size()-48) + "\n";
}
dump += "--------------------\n";
return dump;
}

View File

@ -85,8 +85,8 @@ namespace RNS {
uint8_t EMPTY_DESTINATION[Reticulum::DESTINATION_LENGTH] = {0}; uint8_t EMPTY_DESTINATION[Reticulum::DESTINATION_LENGTH] = {0};
public: public:
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 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 Bytes &transport_id = Bytes::NONE, 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(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 Bytes &transport_id = Bytes::NONE, bool create_receipt = true) : Packet(destination, Interface::NONE, data, packet_type, context, transport_type, header_type, transport_id, create_receipt) {
} }
Packet(NoneConstructor none) { Packet(NoneConstructor none) {
extreme("Packet NONE object created"); extreme("Packet NONE object created");
@ -115,6 +115,7 @@ namespace RNS {
public: public:
uint8_t get_packed_flags(); uint8_t get_packed_flags();
void unpack_flags(uint8_t flags);
void pack(); void pack();
bool unpack(); bool unpack();
bool send(); bool send();
@ -128,12 +129,14 @@ namespace RNS {
// getters/setters // getters/setters
inline const Interface& receiving_interface() const { assert(_object); return _object->_receiving_interface; } inline const Interface& receiving_interface() const { assert(_object); return _object->_receiving_interface; }
std::string toString();
public: public:
types _packet_type; header_types _header_type = HEADER_1;
header_types _header_type; Transport::types _transport_type = Transport::BROADCAST;
context_types _context; Destination::types _destination_type = Destination::SINGLE;
Transport::types _transport_type; types _packet_type = DATA;
Destination::types _destination_type; context_types _context = CONTEXT_NONE;
uint8_t _flags = 0; uint8_t _flags = 0;
uint8_t _hops = 0; uint8_t _hops = 0;
@ -153,13 +156,25 @@ namespace RNS {
//uint8_t _packet_hash[Reticulum::HASHLENGTH] = {0}; //uint8_t _packet_hash[Reticulum::HASHLENGTH] = {0};
Bytes _packet_hash; Bytes _packet_hash;
/*
uint8_t _destination_hash[Reticulum::DESTINATION_LENGTH] = {0}; uint8_t _destination_hash[Reticulum::DESTINATION_LENGTH] = {0};
uint8_t _transport_id[Reticulum::DESTINATION_LENGTH] = {0}; uint8_t _transport_id[Reticulum::DESTINATION_LENGTH] = {0};
// universal packet buffer
uint8_t _raw[Reticulum::MTU]; uint8_t _raw[Reticulum::MTU];
uint8_t _header[Reticulum::HEADER_MAXSIZE]; // header pointer into universal packet buffer
uint8_t *_header = _raw;
uint16_t _header_len = 0;
// data pointer into universal packet buffer
uint8_t *_data = _raw + Reticulum::HEADER_MAXSIZE; uint8_t *_data = _raw + Reticulum::HEADER_MAXSIZE;
uint16_t _data_len = 0; uint16_t _data_len = 0;
uint8_t _raw[Reticulum::MTU];
*/
Bytes _destination_hash;
Bytes _transport_id;
Bytes _raw; // header + ciphertext
Bytes _data; // plaintext
private: private:
class Object { class Object {

View File

@ -7,14 +7,16 @@ void test() {
//RNS::LogLevel loglevel = RNS::loglevel(); //RNS::LogLevel loglevel = RNS::loglevel();
//RNS::loglevel(RNS::LOG_WARNING); //RNS::loglevel(RNS::LOG_WARNING);
testMap();
testBytes(); testBytes();
testCowBytes(); testCowBytes();
testBytesConversion(); testBytesConversion();
testMap();
testReference(); testReference();
testCrypto(); testCrypto();
testHMAC();
testPKCS7();
//RNS::loglevel(loglevel); //RNS::loglevel(loglevel);

View File

@ -9,3 +9,5 @@ void testBytesConversion();
void testReference(); void testReference();
void testCrypto(); void testCrypto();
void testHMAC();
void testPKCS7();

View File

@ -4,54 +4,7 @@
#include "Log.h" #include "Log.h"
#include <map> #include <map>
#include <assert.h>
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::extreme(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::extreme(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::extreme(std::string("found newbuf: ") + (*newit).second);
}
std::string str = map["World"];
assert(str.size() == 5);
assert(str.compare("world") == 0);
}
void testBytes() { void testBytes() {
@ -154,6 +107,22 @@ void testBytes() {
assert(mid.size() == 8); assert(mid.size() == 8);
assert(memcmp(mid.data(), "lo World", mid.size()) == 0); assert(memcmp(mid.data(), "lo World", mid.size()) == 0);
} }
// test mid to end variant
{
RNS::Bytes mid(bytes.mid(3));
RNS::extreme("end mid: " + mid.toString());
assert(mid.size() == 8);
assert(memcmp(mid.data(), "lo World", mid.size()) == 0);
}
// test resize
{
RNS::Bytes shrink(bytes);
shrink.resize(5);
RNS::extreme("shrink: " + shrink.toString());
assert(shrink.size() == 5);
assert(memcmp(shrink.data(), "Hello", shrink.size()) == 0);
}
// stream into empty bytes // stream into empty bytes
{ {
@ -220,6 +189,8 @@ void testBytes() {
assert(bytes.data() == nullptr); assert(bytes.data() == nullptr);
} }
// TODO test comparison
} }
void testCowBytes() { void testCowBytes() {
@ -311,14 +282,62 @@ void testBytesConversion() {
} }
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::extreme(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::extreme(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::extreme(std::string("found newbuf: ") + (*newit).second);
}
std::string str = map["World"];
assert(str.size() == 5);
assert(str.compare("world") == 0);
}
/* /*
int main(void) int main(void)
{ {
UNITY_BEGIN(); UNITY_BEGIN();
RUN_TEST(testMap);
RUN_TEST(testBytes); RUN_TEST(testBytes);
RUN_TEST(testCowBytes); RUN_TEST(testCowBytes);
RUN_TEST(testBytesConversion); RUN_TEST(testBytesConversion);
RUN_TEST(testMap);
return UNITY_END(); return UNITY_END();
} }
*/ */

View File

@ -5,6 +5,12 @@
#include "Destination.h" #include "Destination.h"
#include "Packet.h" #include "Packet.h"
#include "Bytes.h" #include "Bytes.h"
#include "Log.h"
#include "Cryptography/HMAC.h"
#include "Cryptography/PKCS7.h"
#include <assert.h>
void testCrypto() { void testCrypto() {
@ -21,6 +27,105 @@ void testCrypto() {
//Packet packet = destination.announce("appdata"); //Packet packet = destination.announce("appdata");
} }
void testHMAC() {
{
const char keystr[] = "key";
const char datastr[] = "The quick brown fox jumps over the lazy dog";
const uint8_t hasharr[] = {
0xf7, 0xbc, 0x83, 0xf4, 0x30, 0x53, 0x84, 0x24,
0xb1, 0x32, 0x98, 0xe6, 0xaa, 0x6f, 0xb1, 0x43,
0xef, 0x4d, 0x59, 0xa1, 0x49, 0x46, 0x17, 0x59,
0x97, 0x47, 0x9d, 0xbc, 0x2d, 0x1a, 0x3c, 0xd8
};
RNS::Bytes key(keystr);
RNS::Bytes data(datastr);
RNS::Bytes hash(hasharr, sizeof(hasharr));
RNS::extreme("expected hash: " + hash.toHex());
RNS::Cryptography::HMAC hmac(key, data);
RNS::Bytes result = hmac.digest();
RNS::extreme("result hash: " + result.toHex());
assert(memcmp(hash.data(), result.data(), result.size()) == 0);
}
}
void testPKCS7() {
const uint8_t str[] = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
// test buffer of half blocksize
{
size_t len = RNS::Cryptography::PKCS7::BLOCKSIZE / 2;
RNS::Bytes bytes(str, len);
bytes = RNS::Cryptography::PKCS7::pad(bytes);
assert(bytes.size() == RNS::Cryptography::PKCS7::BLOCKSIZE);
bytes = RNS::Cryptography::PKCS7::unpad(bytes);
assert(bytes.size() == len);
assert(memcmp(bytes.data(), str, len) == 0);
}
// test buffer of one less blocksize
{
size_t len = RNS::Cryptography::PKCS7::BLOCKSIZE - 1;
RNS::Bytes bytes(str, len);
bytes = RNS::Cryptography::PKCS7::pad(bytes);
assert(bytes.size() == RNS::Cryptography::PKCS7::BLOCKSIZE);
bytes = RNS::Cryptography::PKCS7::unpad(bytes);
assert(bytes.size() == len);
assert(memcmp(bytes.data(), str, len) == 0);
}
// test buffer of blocksize
{
size_t len = RNS::Cryptography::PKCS7::BLOCKSIZE;
RNS::Bytes bytes(str, len);
bytes = RNS::Cryptography::PKCS7::pad(bytes);
assert(bytes.size() == RNS::Cryptography::PKCS7::BLOCKSIZE * 2);
bytes = RNS::Cryptography::PKCS7::unpad(bytes);
assert(bytes.size() == len);
assert(memcmp(bytes.data(), str, len) == 0);
}
// test inplace buffer of half blocksize
{
size_t len = RNS::Cryptography::PKCS7::BLOCKSIZE / 2;
RNS::Bytes bytes(str, len);
RNS::Cryptography::PKCS7::inplace_pad(bytes);
assert(bytes.size() == RNS::Cryptography::PKCS7::BLOCKSIZE);
RNS::Cryptography::PKCS7::inplace_unpad(bytes);
assert(bytes.size() == len);
assert(memcmp(bytes.data(), str, len) == 0);
}
// test inplace buffer of one less blocksize
{
size_t len = RNS::Cryptography::PKCS7::BLOCKSIZE - 1;
RNS::Bytes bytes(str, len);
RNS::Cryptography::PKCS7::inplace_pad(bytes);
assert(bytes.size() == RNS::Cryptography::PKCS7::BLOCKSIZE);
RNS::Cryptography::PKCS7::inplace_unpad(bytes);
assert(bytes.size() == len);
assert(memcmp(bytes.data(), str, len) == 0);
}
// test inplace buffer of blocksize
{
size_t len = RNS::Cryptography::PKCS7::BLOCKSIZE;
RNS::Bytes bytes(str, len);
RNS::Cryptography::PKCS7::inplace_pad(bytes);
assert(bytes.size() == RNS::Cryptography::PKCS7::BLOCKSIZE * 2);
RNS::Cryptography::PKCS7::inplace_unpad(bytes);
assert(bytes.size() == len);
assert(memcmp(bytes.data(), str, len) == 0);
}
}
/* /*
int main(void) int main(void)
{ {

View File

@ -3,6 +3,8 @@
#include "Reticulum.h" #include "Reticulum.h"
#include "Bytes.h" #include "Bytes.h"
#include <assert.h>
void testReference() { void testReference() {
RNS::Reticulum reticulum_default; RNS::Reticulum reticulum_default;

View File

@ -19,8 +19,6 @@
#include <map> #include <map>
//#include <sstream> //#include <sstream>
#include <assert.h>
// Let's define an app name. We'll use this for all // Let's define an app name. We'll use this for all
// destinations we create. Since this basic example // destinations we create. Since this basic example
// is part of a range of example utilities, we'll put // is part of a range of example utilities, we'll put
@ -68,9 +66,20 @@ void setup() {
//destination.announce(RNS::bytesFromString(fruits[rand() % 7])); //destination.announce(RNS::bytesFromString(fruits[rand() % 7]));
// test path // test path
destination.announce(RNS::bytesFromString(fruits[rand() % 7]), true, nullptr, RNS::bytesFromString("test_tag")); //destination.announce(RNS::bytesFromString(fruits[rand() % 7]), true, nullptr, RNS::bytesFromString("test_tag"));
// test packet send
RNS::Packet send_packet = destination.announce(RNS::bytesFromString(fruits[rand() % 7]));
//RNS::Packet packet = destination.announce(RNS::bytesFromString(fruits[rand() % 7]), false, nullptr, RNS::Bytes::NONE, false);
// 23.9% (+0.8%) // 23.9% (+0.8%)
RNS::extreme("Test send_packet packet: " + send_packet.toString());
// test packet receive
RNS::Packet recv_packet(RNS::Destination::NONE, send_packet._raw);
RNS::extreme("Test recv_packet packet: " + recv_packet.toString());
destination.receive(recv_packet);
//zdestination.set_proof_strategy(RNS::Destination::PROVE_ALL); //zdestination.set_proof_strategy(RNS::Destination::PROVE_ALL);
//zannounce_handler = ExampleAnnounceHandler( //zannounce_handler = ExampleAnnounceHandler(