mirror of
https://github.com/markqvist/reticulum-cpp.git
synced 2024-10-01 02:55:46 -04:00
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:
parent
4b062aa485
commit
f9e3a22911
@ -28,7 +28,7 @@ build_flags =
|
||||
-Isrc
|
||||
-DNATIVE
|
||||
lib_deps =
|
||||
; operatorfoundation/Crypto@^0.4.0
|
||||
; rweather/Crypto@^0.4.0
|
||||
lib_compat_mode = off
|
||||
|
||||
[env:ttgo-t-beam]
|
||||
@ -43,4 +43,4 @@ build_flags =
|
||||
-Wno-format
|
||||
-Isrc
|
||||
lib_deps =
|
||||
operatorfoundation/Crypto@^0.4.0
|
||||
rweather/Crypto@^0.4.0
|
||||
|
@ -34,15 +34,19 @@ int8_t Bytes::compare(const Bytes &bytes) const {
|
||||
else if (*_data < *(bytes._data)) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
else if (*_data > *(bytes._data)) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Bytes::assignHex(const char* hex) {
|
||||
// if assignment is empty then clear data and don't bother creating new
|
||||
if (hex == nullptr || hex[0] == 0) {
|
||||
_data = nullptr;
|
||||
_owner = true;
|
||||
return;
|
||||
}
|
||||
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' };
|
||||
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_upper_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 {
|
||||
if (!_data) {
|
||||
@ -87,12 +104,21 @@ std::string Bytes::toHex(bool upper /*= true*/) const {
|
||||
return hex;
|
||||
}
|
||||
|
||||
Bytes Bytes::mid(size_t pos, size_t len) const {
|
||||
if (!_data || pos >= size()) {
|
||||
// mid
|
||||
Bytes Bytes::mid(size_t beginpos, size_t len) const {
|
||||
if (!_data || beginpos >= size()) {
|
||||
return NONE;
|
||||
}
|
||||
if ((pos + len) >= size()) {
|
||||
len = (size() - pos);
|
||||
if ((beginpos + len) >= size()) {
|
||||
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};
|
||||
}
|
||||
|
46
src/Bytes.h
46
src/Bytes.h
@ -104,6 +104,11 @@ namespace RNS {
|
||||
void ownData();
|
||||
|
||||
public:
|
||||
inline void clear() {
|
||||
_data = nullptr;
|
||||
_owner = true;
|
||||
}
|
||||
|
||||
inline void assign(const Bytes& bytes) {
|
||||
#ifdef COW
|
||||
_data = bytes.shareData();
|
||||
@ -112,6 +117,7 @@ namespace RNS {
|
||||
// if assignment is empty then clear data and don't bother creating new
|
||||
if (bytes.size() <= 0) {
|
||||
_data = nullptr;
|
||||
_owner = true;
|
||||
return;
|
||||
}
|
||||
newData();
|
||||
@ -122,6 +128,7 @@ namespace RNS {
|
||||
// if assignment is empty then clear data and don't bother creating new
|
||||
if (chunk == nullptr || size <= 0) {
|
||||
_data = nullptr;
|
||||
_owner = true;
|
||||
return;
|
||||
}
|
||||
newData();
|
||||
@ -131,6 +138,7 @@ namespace RNS {
|
||||
// if assignment is empty then clear data and don't bother creating new
|
||||
if (string == nullptr || string[0] == 0) {
|
||||
_data = nullptr;
|
||||
_owner = true;
|
||||
return;
|
||||
}
|
||||
newData();
|
||||
@ -170,6 +178,20 @@ namespace RNS {
|
||||
}
|
||||
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:
|
||||
int8_t compare(const Bytes &bytes) const;
|
||||
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()}; }
|
||||
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 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;
|
||||
@ -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
|
||||
//static inline Bytes bytesFromArray(const uint8_t arr[]) { return Bytes(arr, sizeof(arr)); }
|
||||
//static inline Bytes bytesFromChunk(const uint8_t *ptr, size_t len) { return Bytes(ptr, len); }
|
||||
static inline Bytes bytesFromChunk(const uint8_t *ptr, size_t len) { return {ptr, len}; }
|
||||
//static inline Bytes bytesFromString(const char *str) { return Bytes((uint8_t*)str, strlen(str)); }
|
||||
static inline Bytes bytesFromString(const char *str) { return {(uint8_t*)str, strlen(str)}; }
|
||||
//zstatic inline Bytes bytesFromInt(const int) { return {(uint8_t*)str, strlen(str)}; }
|
||||
//inline Bytes bytesFromArray(const uint8_t arr[]) { return Bytes(arr, sizeof(arr)); }
|
||||
//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 {ptr, len}; }
|
||||
//inline Bytes bytesFromString(const char *str) { return Bytes((uint8_t*)str, strlen(str)); }
|
||||
inline Bytes bytesFromString(const char *str) { 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
50
src/Cryptography/AES.h
Normal 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
171
src/Cryptography/CBC.cpp
Normal 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
66
src/Cryptography/CBC.h
Normal 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
|
@ -1,14 +1,138 @@
|
||||
#include "Fernet.h"
|
||||
|
||||
#include "HMAC.h"
|
||||
#include "PKCS7.h"
|
||||
#include "AES.h"
|
||||
#include "../Log.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <time.h>
|
||||
|
||||
using namespace RNS;
|
||||
using namespace RNS::Cryptography;
|
||||
|
||||
Fernet::Fernet() {
|
||||
log("Fernet object created", LOG_EXTREME);
|
||||
Fernet::Fernet(const Bytes &key) {
|
||||
|
||||
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() {
|
||||
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");
|
||||
}
|
||||
}
|
@ -1,18 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "Random.h"
|
||||
#include "../Bytes.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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 {
|
||||
|
||||
public:
|
||||
static const uint8_t FERNET_OVERHEAD = 48; // Bytes
|
||||
|
||||
public:
|
||||
Fernet();
|
||||
static inline Bytes generate_key() { return random(32); }
|
||||
|
||||
public:
|
||||
Fernet(const Bytes &key);
|
||||
~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
28
src/Cryptography/HKDF.cpp
Normal 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
9
src/Cryptography/HKDF.h
Normal 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
108
src/Cryptography/HMAC.h
Normal 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();
|
||||
}
|
||||
|
||||
} }
|
@ -1,6 +1,5 @@
|
||||
#include "Hashes.h"
|
||||
|
||||
#include "../Log.h"
|
||||
#include "../Bytes.h"
|
||||
|
||||
#include <SHA256.h>
|
||||
|
61
src/Cryptography/PKCS7.h
Normal file
61
src/Cryptography/PKCS7.h
Normal 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
16
src/Cryptography/Random.h
Normal 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;
|
||||
}
|
||||
|
||||
} }
|
@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "Bytes.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <Curve25519.h>
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace RNS { namespace Cryptography {
|
||||
@ -167,11 +169,29 @@ namespace RNS { namespace Cryptography {
|
||||
}
|
||||
*/
|
||||
inline Bytes exchange(const Bytes &peer_public_key) {
|
||||
Bytes sharedKey(peer_public_key);
|
||||
Curve25519::dh2(sharedKey.writable(32), _privateKey.writable(32));
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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:
|
||||
//Bytes _a;
|
||||
Bytes _privateKey;
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
using namespace RNS;
|
||||
@ -42,13 +41,12 @@ Destination::Destination(const Identity &identity, const directions direction, c
|
||||
// Generate the destination address hash
|
||||
debug("Destination::Destination: creating hash...");
|
||||
_object->_hash = hash(_object->_identity, app_name, fullaspects.c_str());
|
||||
_object->_hexhash = _object->_hash.toHex();
|
||||
debug("Destination::Destination: hash: " + _object->_hash.toHex());
|
||||
// CBA TEST CRASH
|
||||
debug("Destination::Destination: creating name hash...");
|
||||
_object->_name_hash = Identity::truncated_hash(expand_name(Identity::NONE, app_name, fullaspects.c_str()));
|
||||
debug("Destination::Destination: name hash: " + _object->_name_hash.toHex());
|
||||
_object->_hexhash = _object->_hash.toHex();
|
||||
debug("Destination::Destination: hexhash: " + _object->_hexhash);
|
||||
|
||||
debug("Destination::Destination: calling register_destination");
|
||||
Transport::register_destination(*this);
|
||||
@ -178,7 +176,7 @@ Packet Destination::announce(const Bytes &app_data, bool path_response, Interfac
|
||||
}
|
||||
else {
|
||||
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 new_app_data(app_data);
|
||||
@ -188,7 +186,7 @@ Packet Destination::announce(const Bytes &app_data, bool path_response, Interfac
|
||||
|
||||
Bytes signed_data;
|
||||
debug("Destination::announce: hash: " + _object->_hash.toHex());
|
||||
debug("Destination::announce: identity public key: " + _object->_identity.get_public_key().toHex());
|
||||
debug("Destination::announce: public key: " + _object->_identity.get_public_key().toHex());
|
||||
debug("Destination::announce: name hash: " + _object->_name_hash.toHex());
|
||||
debug("Destination::announce: random hash: " + random_hash.toHex());
|
||||
debug("Destination::announce: new app data: " + new_app_data.toHex());
|
||||
@ -217,49 +215,99 @@ Packet Destination::announce(const Bytes &app_data, bool path_response, Interfac
|
||||
}
|
||||
|
||||
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) {
|
||||
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 {
|
||||
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);
|
||||
_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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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) {
|
||||
void Destination::incoming_link_request(const Bytes &data, const Packet &packet) {
|
||||
assert(_object);
|
||||
_object->_callbacks._packet = callback;
|
||||
if (_object->_accept_link_requests) {
|
||||
//zlink = RNS::Link::validate_request(data, packet);
|
||||
//zif (link) {
|
||||
//z _links.append(link);
|
||||
//z}
|
||||
}
|
||||
|
||||
/*
|
||||
Registers a function to be called when a proof has been requested for
|
||||
a packet sent to this destination. Allows control over when and if
|
||||
proofs should be returned for received packets.
|
||||
|
||||
:param callback: A function or method to with the signature *callback(packet)* be called when a packet that requests a proof is received. The callback must return one of True or False. If the callback returns True, a proof will be sent. If it returns False, a proof will not be sent.
|
||||
*/
|
||||
void Destination::set_proof_requested_callback(Callbacks::proof_requested callback) {
|
||||
assert(_object);
|
||||
_object->_callbacks._proof_requested = callback;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -270,7 +318,7 @@ Encrypts information for ``RNS.Destination.SINGLE`` or ``RNS.Destination.GROUP``
|
||||
*/
|
||||
Bytes Destination::encrypt(const Bytes &data) {
|
||||
assert(_object);
|
||||
debug("Destination::encrypt: encrypting bytes");
|
||||
debug("Destination::encrypt: encrypting data...");
|
||||
|
||||
if (_object->_type == Destination::PLAIN) {
|
||||
return data;
|
||||
@ -304,7 +352,7 @@ Decrypts information for ``RNS.Destination.SINGLE`` or ``RNS.Destination.GROUP``
|
||||
*/
|
||||
Bytes Destination::decrypt(const Bytes &data) {
|
||||
assert(_object);
|
||||
debug("Destination::decrypt: decrypting bytes");
|
||||
debug("Destination::decrypt: decrypting data...");
|
||||
|
||||
if (_object->_type == Destination::PLAIN) {
|
||||
return data;
|
||||
|
@ -4,12 +4,13 @@
|
||||
#include "Identity.h"
|
||||
#include "Bytes.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace RNS {
|
||||
|
||||
@ -36,14 +37,15 @@ namespace RNS {
|
||||
public:
|
||||
class Callbacks {
|
||||
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(*)(const Bytes &data, Packet *packet);
|
||||
using proof_requested = void(*)(Packet *packet);
|
||||
|
||||
using packet = void(*)(const Bytes &data, const Packet &packet);
|
||||
using proof_requested = void(*)(const Packet &packet);
|
||||
public:
|
||||
link_established _link_established = nullptr;
|
||||
packet _packet = nullptr;
|
||||
proof_requested _proof_requested = nullptr;
|
||||
friend class Detination;
|
||||
};
|
||||
|
||||
//typedef std::pair<time_t, std::string> Response;
|
||||
@ -106,11 +108,64 @@ namespace RNS {
|
||||
|
||||
public:
|
||||
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 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 decrypt(const Bytes &data);
|
||||
Bytes sign(const Bytes &message);
|
||||
@ -124,6 +179,8 @@ namespace RNS {
|
||||
inline uint16_t mtu() const { assert(_object); return _object->_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:
|
||||
class Object {
|
||||
public:
|
||||
|
169
src/Identity.cpp
169
src/Identity.cpp
@ -3,8 +3,10 @@
|
||||
#include "Reticulum.h"
|
||||
#include "Packet.h"
|
||||
#include "Log.h"
|
||||
#include "Cryptography/Hashes.h"
|
||||
#include "Cryptography/X25519.h"
|
||||
#include "Cryptography/HKDF.h"
|
||||
#include "Cryptography/Fernet.h"
|
||||
#include "Cryptography/Random.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@ -21,18 +23,22 @@ Identity::Identity(bool create_keys) : _object(new Object()) {
|
||||
void Identity::createKeys() {
|
||||
assert(_object);
|
||||
|
||||
// CRYPTO: create encryption private keys
|
||||
_object->_prv = Cryptography::X25519PrivateKey::generate();
|
||||
_object->_prv_bytes = _object->_prv->private_bytes();
|
||||
debug("Identity::createKeys: prv bytes: " + _object->_prv_bytes.toHex());
|
||||
|
||||
_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 encryption public keys
|
||||
_object->_pub = _object->_prv->public_key();
|
||||
_object->_pub_bytes = _object->_pub->public_bytes();
|
||||
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_bytes = _object->_sig_pub->public_bytes();
|
||||
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());
|
||||
}
|
||||
|
||||
/*
|
||||
: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.
|
||||
@ -89,31 +58,34 @@ Encrypts information for the identity.
|
||||
*/
|
||||
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 {
|
||||
debug("Identity::encrypt: encrypting data...");
|
||||
if (!_object->_pub) {
|
||||
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,43 +98,50 @@ Decrypts information for the identity.
|
||||
*/
|
||||
Bytes Identity::decrypt(const Bytes &ciphertext_token) {
|
||||
assert(_object);
|
||||
if (_object->_prv) {
|
||||
if (ciphertext_token.size() > Identity::KEYSIZE/8/2) {
|
||||
debug("Identity::decrypt: decrypting data...");
|
||||
if (!_object->_prv) {
|
||||
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 {
|
||||
Bytes peer_pub_bytes = ciphertext_token.right(Identity::KEYSIZE/8/2);
|
||||
Cryptography::X25519PublicKey::Ptr peer_pub = Cryptography::X25519PublicKey::from_public_bytes(peer_pub_bytes);
|
||||
//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());
|
||||
|
||||
/*
|
||||
Bytes shared_key = _object->_prv->exchange(peer_pub);
|
||||
// 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 = RNS.Cryptography.hkdf(
|
||||
length=32,
|
||||
derive_from=shared_key,
|
||||
salt=get_salt(),
|
||||
context=get_context(),
|
||||
)
|
||||
Bytes derived_key = Cryptography::hkdf(
|
||||
32,
|
||||
shared_key,
|
||||
get_salt(),
|
||||
get_context()
|
||||
);
|
||||
debug("Identity::decrypt: derived key: " + derived_key.toHex());
|
||||
|
||||
fernet = Fernet(derived_key)
|
||||
ciphertext = ciphertext_token[Identity.KEYSIZE//8//2:]
|
||||
plaintext = fernet.decrypt(ciphertext)
|
||||
*/
|
||||
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 " + _object->_hash.toHex() + " failed: " + e.what());
|
||||
debug("Decryption by " + toString() + " 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.
|
||||
@ -173,7 +152,9 @@ Signs information by the identity.
|
||||
*/
|
||||
Bytes Identity::sign(const Bytes &message) {
|
||||
assert(_object);
|
||||
if (_object->_sig_prv) {
|
||||
if (!_object->_sig_prv) {
|
||||
throw std::runtime_error("Signing failed because identity does not hold a private key");
|
||||
}
|
||||
try {
|
||||
return _object->_sig_prv->sign(message);
|
||||
}
|
||||
@ -182,10 +163,6 @@ Bytes Identity::sign(const Bytes &message) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Signing failed because identity does not hold a private key");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Validates the signature of a signed message.
|
||||
|
@ -5,9 +5,10 @@
|
||||
//#include "Destination.h"
|
||||
#include "Log.h"
|
||||
#include "Bytes.h"
|
||||
#include "Cryptography/Fernet.h"
|
||||
#include "Cryptography/X25519.h"
|
||||
#include "Cryptography/Hashes.h"
|
||||
#include "Cryptography/Ed25519.h"
|
||||
#include "Cryptography/X25519.h"
|
||||
#include "Cryptography/Fernet.h"
|
||||
|
||||
|
||||
#include <memory>
|
||||
@ -66,11 +67,51 @@ namespace RNS {
|
||||
|
||||
public:
|
||||
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 decrypt(const Bytes &ciphertext_token);
|
||||
@ -87,7 +128,7 @@ namespace RNS {
|
||||
inline Bytes hash() const { assert(_object); return _object->_hash; }
|
||||
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:
|
||||
class Object {
|
||||
|
@ -45,6 +45,7 @@ void RNS::doLog(const char* msg, LogLevel level) {
|
||||
Serial.print(getLevelName(level));
|
||||
Serial.print(" ");
|
||||
Serial.println(msg);
|
||||
Serial.flush();
|
||||
#else
|
||||
printf("%s: %s\n", getLevelName(level), msg);
|
||||
#endif
|
||||
|
302
src/Packet.cpp
302
src/Packet.cpp
@ -8,10 +8,10 @@
|
||||
|
||||
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 uint8_t *transport_id /*= nullptr*/, 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 Bytes &transport_id /*= Bytes::NONE*/, bool create_receipt /*= true*/) : _object(new Object(destination, attached_interface)) {
|
||||
|
||||
if (_object->_destination) {
|
||||
extreme("Creating packet with detination...");
|
||||
// CBA TODO handle NONE
|
||||
if (transport_type == -1) {
|
||||
transport_type = Transport::BROADCAST;
|
||||
@ -23,31 +23,37 @@ Packet::Packet(const Destination &destination, const Interface &attached_interfa
|
||||
_transport_type = transport_type;
|
||||
_context = context;
|
||||
|
||||
//transport_id = transport_id;
|
||||
//setTransportId(transport_id);
|
||||
if (transport_id != nullptr) {
|
||||
memcpy(_transport_id, transport_id, Reticulum::DESTINATION_LENGTH);
|
||||
}
|
||||
_transport_id = transport_id;
|
||||
|
||||
//data = data;
|
||||
//setData(data);
|
||||
_data = data;
|
||||
if (_data.size() > MDU) {
|
||||
_truncated = true;
|
||||
_data.resize(MDU);
|
||||
}
|
||||
/*
|
||||
if (data) {
|
||||
// data is plaintext
|
||||
if (data.size() > MDU) {
|
||||
_truncated = true;
|
||||
// CBA TODO add method to truncate
|
||||
//zdata_len = MDU;
|
||||
}
|
||||
_data = _raw + Reticulum::HEADER_MAXSIZE;
|
||||
memcpy(_data, data.data(), data.size());
|
||||
}
|
||||
*/
|
||||
_flags = get_packed_flags();
|
||||
|
||||
_create_receipt = create_receipt;
|
||||
}
|
||||
else {
|
||||
//_raw = data;
|
||||
extreme("Creating packet without detination...");
|
||||
_raw = data;
|
||||
/*
|
||||
if (data) {
|
||||
memcpy(_raw, data.data(), data.size());
|
||||
}
|
||||
*/
|
||||
_packed = true;
|
||||
_fromPacked = true;
|
||||
_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 packed_flags = 0;
|
||||
if (_context == LRPROOF) {
|
||||
@ -122,6 +77,13 @@ uint8_t Packet::get_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 ======
|
||||
@ -287,20 +249,22 @@ but excluding any interface access codes.
|
||||
// | ...destination_2 | context | data ... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
/*
|
||||
void Packet::pack() {
|
||||
assert(_object);
|
||||
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, _object->_destination.hash().data(), _object->_destination.hash().size());
|
||||
|
||||
_header[0] = _flags;
|
||||
_header[1] = _hops;
|
||||
|
||||
//uint8_t *ciphertext;
|
||||
if (_context == LRPROOF) {
|
||||
// write header
|
||||
//memcpy(_header+2, _destination->_link_id, Reticulum::DESTINATION_LENGTH);
|
||||
_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() );
|
||||
memcpy(_header+2, _object->_destination.link_id().data(), _object->_destination.link_id().size());
|
||||
_header[Reticulum::DESTINATION_LENGTH+2] = _context;
|
||||
@ -311,7 +275,10 @@ void Packet::pack() {
|
||||
else {
|
||||
if (_header_type == HEADER_1) {
|
||||
// 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() );
|
||||
memcpy(_header+2, _object->_destination.hash().data(), _object->_destination.hash().size());
|
||||
_header[Reticulum::DESTINATION_LENGTH+2] = _context;
|
||||
@ -359,8 +326,9 @@ void Packet::pack() {
|
||||
//memcpy(data, _data, _data_len);
|
||||
//_data_len = _destination->encrypt(_data, data, _data_len);
|
||||
Bytes plaintext(_data, _data_len);
|
||||
Bytes bytes = _object->_destination.encrypt(plaintext);
|
||||
_data_len = bytes.size();
|
||||
Bytes ciphertext = _object->_destination.encrypt(plaintext);
|
||||
memcpy(_data, ciphertext.data(), ciphertext.size());
|
||||
_data_len = ciphertext.size();
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
// write header
|
||||
_header = _data - Reticulum::HEADER_MAXSIZE;
|
||||
_header[0] = _flags;
|
||||
_header[1] = _hops;
|
||||
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() );
|
||||
memcpy(_header+Reticulum::DESTINATION_LENGTH+2, _object->_destination.hash().data(), _object->_destination.hash().size());
|
||||
_header[2*Reticulum::DESTINATION_LENGTH+2] = _context;
|
||||
@ -390,12 +361,104 @@ void Packet::pack() {
|
||||
_packed = true;
|
||||
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() {
|
||||
assert(_object);
|
||||
debug("Packet::unpack: unpacking packet...");
|
||||
extreme("Packet::unpack: pre hops: " + std::to_string(_hops));
|
||||
try {
|
||||
|
||||
// read header
|
||||
_flags = _raw[0];
|
||||
_hops = _raw[1];
|
||||
|
||||
@ -431,6 +494,58 @@ bool Packet::unpack() {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -525,8 +640,15 @@ Bytes Packet::getTruncatedHash() {
|
||||
Bytes Packet::get_hashable_part() {
|
||||
assert(_object);
|
||||
Bytes hashable_part;
|
||||
hashable_part << (uint8_t)(_raw[0] & 0b00001111);
|
||||
hashable_part.append(_data-Reticulum::DESTINATION_LENGTH-1, _data_len+Reticulum::DESTINATION_LENGTH+1);
|
||||
hashable_part << (uint8_t)(_raw.data()[0] & 0b00001111);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -536,3 +658,41 @@ Bytes Packet::get_hashable_part() {
|
||||
// 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;
|
||||
}
|
||||
|
31
src/Packet.h
31
src/Packet.h
@ -85,8 +85,8 @@ namespace RNS {
|
||||
uint8_t EMPTY_DESTINATION[Reticulum::DESTINATION_LENGTH] = {0};
|
||||
|
||||
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 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 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 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) {
|
||||
extreme("Packet NONE object created");
|
||||
@ -115,6 +115,7 @@ namespace RNS {
|
||||
|
||||
public:
|
||||
uint8_t get_packed_flags();
|
||||
void unpack_flags(uint8_t flags);
|
||||
void pack();
|
||||
bool unpack();
|
||||
bool send();
|
||||
@ -128,12 +129,14 @@ namespace RNS {
|
||||
// getters/setters
|
||||
inline const Interface& receiving_interface() const { assert(_object); return _object->_receiving_interface; }
|
||||
|
||||
std::string toString();
|
||||
|
||||
public:
|
||||
types _packet_type;
|
||||
header_types _header_type;
|
||||
context_types _context;
|
||||
Transport::types _transport_type;
|
||||
Destination::types _destination_type;
|
||||
header_types _header_type = HEADER_1;
|
||||
Transport::types _transport_type = Transport::BROADCAST;
|
||||
Destination::types _destination_type = Destination::SINGLE;
|
||||
types _packet_type = DATA;
|
||||
context_types _context = CONTEXT_NONE;
|
||||
|
||||
uint8_t _flags = 0;
|
||||
uint8_t _hops = 0;
|
||||
@ -153,13 +156,25 @@ namespace RNS {
|
||||
|
||||
//uint8_t _packet_hash[Reticulum::HASHLENGTH] = {0};
|
||||
Bytes _packet_hash;
|
||||
/*
|
||||
uint8_t _destination_hash[Reticulum::DESTINATION_LENGTH] = {0};
|
||||
uint8_t _transport_id[Reticulum::DESTINATION_LENGTH] = {0};
|
||||
|
||||
// universal packet buffer
|
||||
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;
|
||||
uint16_t _data_len = 0;
|
||||
uint8_t _raw[Reticulum::MTU];
|
||||
*/
|
||||
Bytes _destination_hash;
|
||||
Bytes _transport_id;
|
||||
|
||||
Bytes _raw; // header + ciphertext
|
||||
Bytes _data; // plaintext
|
||||
|
||||
private:
|
||||
class Object {
|
||||
|
@ -7,14 +7,16 @@ void test() {
|
||||
//RNS::LogLevel loglevel = RNS::loglevel();
|
||||
//RNS::loglevel(RNS::LOG_WARNING);
|
||||
|
||||
testMap();
|
||||
testBytes();
|
||||
testCowBytes();
|
||||
testBytesConversion();
|
||||
testMap();
|
||||
|
||||
testReference();
|
||||
|
||||
testCrypto();
|
||||
testHMAC();
|
||||
testPKCS7();
|
||||
|
||||
//RNS::loglevel(loglevel);
|
||||
|
||||
|
@ -9,3 +9,5 @@ void testBytesConversion();
|
||||
void testReference();
|
||||
|
||||
void testCrypto();
|
||||
void testHMAC();
|
||||
void testPKCS7();
|
||||
|
@ -4,54 +4,7 @@
|
||||
#include "Log.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
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);
|
||||
}
|
||||
#include <assert.h>
|
||||
|
||||
void testBytes() {
|
||||
|
||||
@ -154,6 +107,22 @@ void testBytes() {
|
||||
assert(mid.size() == 8);
|
||||
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
|
||||
{
|
||||
@ -220,6 +189,8 @@ void testBytes() {
|
||||
assert(bytes.data() == nullptr);
|
||||
}
|
||||
|
||||
// TODO test comparison
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(testMap);
|
||||
RUN_TEST(testBytes);
|
||||
RUN_TEST(testCowBytes);
|
||||
RUN_TEST(testBytesConversion);
|
||||
RUN_TEST(testMap);
|
||||
return UNITY_END();
|
||||
}
|
||||
*/
|
||||
|
@ -5,6 +5,12 @@
|
||||
#include "Destination.h"
|
||||
#include "Packet.h"
|
||||
#include "Bytes.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include "Cryptography/HMAC.h"
|
||||
#include "Cryptography/PKCS7.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void testCrypto() {
|
||||
|
||||
@ -21,6 +27,105 @@ void testCrypto() {
|
||||
//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)
|
||||
{
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include "Reticulum.h"
|
||||
#include "Bytes.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void testReference() {
|
||||
|
||||
RNS::Reticulum reticulum_default;
|
||||
|
15
src/main.cpp
15
src/main.cpp
@ -19,8 +19,6 @@
|
||||
#include <map>
|
||||
//#include <sstream>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
// Let's define an app name. We'll use this for all
|
||||
// destinations we create. Since this basic example
|
||||
// is part of a range of example utilities, we'll put
|
||||
@ -68,9 +66,20 @@ void setup() {
|
||||
|
||||
//destination.announce(RNS::bytesFromString(fruits[rand() % 7]));
|
||||
// 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%)
|
||||
|
||||
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);
|
||||
|
||||
//zannounce_handler = ExampleAnnounceHandler(
|
||||
|
Loading…
Reference in New Issue
Block a user