v1.0.0: Boundary mode with bidirectional LoRa↔TCP transport
Vendor microReticulum library with boundary mode transport fixes: - Two-whitelist system gates backbone traffic (local addresses + mentioned addresses from local devices) - Allow control_hashes and local destinations through boundary filter (fixes backbone→LoRa path discovery) - Fix get_cached_packet() to call unpack() instead of update_hash() (fixes empty destination_hash in path responses) - LRPROOF Identity::recall null guard - remaining_hops HEADER_1/BROADCAST fix for final-hop delivery - PROOF packets excluded from boundary wrapping - Iterator invalidation fix in transport table cleanup - is_backbone flag replaces string matching for interface identification Firmware changes: - Set is_backbone(true) on backbone TCP interface - Rename default TcpInterface name to BackboneInterface - Update comments for dual-use TcpInterface (backbone + local AP) - Use vendored lib/microReticulum instead of PlatformIO registry
This commit is contained in:
89
lib/microReticulum/src/Cryptography/AES.h
Executable file
89
lib/microReticulum/src/Cryptography/AES.h
Executable file
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include "CBC.h"
|
||||
|
||||
#include "../Bytes.h"
|
||||
|
||||
#include <AES.h>
|
||||
|
||||
namespace RNS { namespace Cryptography {
|
||||
|
||||
class AES_128_CBC {
|
||||
|
||||
public:
|
||||
static inline const 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 const 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());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class AES_256_CBC {
|
||||
|
||||
public:
|
||||
static inline const Bytes encrypt(const Bytes& plaintext, const Bytes& key, const Bytes& iv) {
|
||||
CBC<AES256> 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 const Bytes decrypt(const Bytes& ciphertext, const Bytes& key, const Bytes& iv) {
|
||||
CBC<AES256> 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<AES256> 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<AES256> cbc;
|
||||
cbc.setKey(key.data(), key.size());
|
||||
cbc.setIV(iv.data(), iv.size());
|
||||
cbc.decrypt((uint8_t*)ciphertext.data(), ciphertext.data(), ciphertext.size());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} }
|
||||
171
lib/microReticulum/src/Cryptography/CBC.cpp
Executable file
171
lib/microReticulum/src/Cryptography/CBC.cpp
Executable 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
lib/microReticulum/src/Cryptography/CBC.h
Executable file
66
lib/microReticulum/src/Cryptography/CBC.h
Executable 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
|
||||
3
lib/microReticulum/src/Cryptography/Ed25519.cpp
Executable file
3
lib/microReticulum/src/Cryptography/Ed25519.cpp
Executable file
@@ -0,0 +1,3 @@
|
||||
#include "Ed25519.h"
|
||||
|
||||
using namespace RNS::Cryptography;
|
||||
102
lib/microReticulum/src/Cryptography/Ed25519.h
Executable file
102
lib/microReticulum/src/Cryptography/Ed25519.h
Executable file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include "Bytes.h"
|
||||
|
||||
#include <Ed25519.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
/*
|
||||
|
||||
Note that the library currently in use for Ed25519 does not support generating keys from a seed.
|
||||
|
||||
*/
|
||||
|
||||
namespace RNS { namespace Cryptography {
|
||||
|
||||
class Ed25519PublicKey {
|
||||
|
||||
public:
|
||||
using Ptr = std::shared_ptr<Ed25519PublicKey>;
|
||||
|
||||
public:
|
||||
Ed25519PublicKey(const Bytes& publicKey) {
|
||||
_publicKey = publicKey;
|
||||
}
|
||||
~Ed25519PublicKey() {}
|
||||
|
||||
public:
|
||||
// creates a new instance with specified seed
|
||||
static inline Ptr from_public_bytes(const Bytes& publicKey) {
|
||||
return Ptr(new Ed25519PublicKey(publicKey));
|
||||
}
|
||||
|
||||
inline const Bytes& public_bytes() {
|
||||
return _publicKey;
|
||||
}
|
||||
|
||||
inline bool verify(const Bytes& signature, const Bytes& message) {
|
||||
return Ed25519::verify(signature.data(), _publicKey.data(), message.data(), message.size());
|
||||
}
|
||||
|
||||
private:
|
||||
Bytes _publicKey;
|
||||
|
||||
};
|
||||
|
||||
class Ed25519PrivateKey {
|
||||
|
||||
public:
|
||||
using Ptr = std::shared_ptr<Ed25519PrivateKey>;
|
||||
|
||||
public:
|
||||
Ed25519PrivateKey(const Bytes& privateKey) {
|
||||
if (privateKey) {
|
||||
// use specified private key
|
||||
_privateKey = privateKey;
|
||||
}
|
||||
else {
|
||||
// create random private key
|
||||
Ed25519::generatePrivateKey(_privateKey.writable(32));
|
||||
}
|
||||
// derive public key from private key
|
||||
Ed25519::derivePublicKey(_publicKey.writable(32), _privateKey.data());
|
||||
}
|
||||
~Ed25519PrivateKey() {}
|
||||
|
||||
public:
|
||||
// creates a new instance with a random seed
|
||||
static inline Ptr generate() {
|
||||
// CBA TODO determine why below is confused with (implicit) copy constructor
|
||||
//return Ptr(new Ed25519PrivateKey({Bytes::NONE}));
|
||||
return Ptr(new Ed25519PrivateKey(Bytes::NONE));
|
||||
}
|
||||
|
||||
// creates a new instance with specified seed
|
||||
static inline Ptr from_private_bytes(const Bytes& privateKey) {
|
||||
return Ptr(new Ed25519PrivateKey(privateKey));
|
||||
}
|
||||
|
||||
inline const Bytes& private_bytes() {
|
||||
return _privateKey;
|
||||
}
|
||||
|
||||
// creates a new instance of public key for this private key
|
||||
inline Ed25519PublicKey::Ptr public_key() {
|
||||
return Ed25519PublicKey::from_public_bytes(_publicKey);
|
||||
}
|
||||
|
||||
inline const Bytes sign(const Bytes& message) {
|
||||
//z return _sk.sign(message);
|
||||
Bytes signature;
|
||||
Ed25519::sign(signature.writable(64), _privateKey.data(), _publicKey.data(), message.data(), message.size());
|
||||
return signature;
|
||||
}
|
||||
|
||||
private:
|
||||
Bytes _privateKey;
|
||||
Bytes _publicKey;
|
||||
|
||||
};
|
||||
|
||||
} }
|
||||
116
lib/microReticulum/src/Cryptography/Fernet.cpp
Executable file
116
lib/microReticulum/src/Cryptography/Fernet.cpp
Executable file
@@ -0,0 +1,116 @@
|
||||
#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(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()));
|
||||
}
|
||||
|
||||
//self._signing_key = key[:16]
|
||||
_signing_key = key.left(16);
|
||||
//self._encryption_key = key[16:]
|
||||
_encryption_key = key.mid(16);
|
||||
|
||||
MEM("Fernet object created");
|
||||
}
|
||||
|
||||
Fernet::~Fernet() {
|
||||
MEM("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);
|
||||
}
|
||||
|
||||
const Bytes Fernet::encrypt(const Bytes& data) {
|
||||
|
||||
DEBUG("Fernet::encrypt: plaintext length: " + std::to_string(data.size()));
|
||||
Bytes iv = random(16);
|
||||
//double current_time = OS::time();
|
||||
TRACE("Fernet::encrypt: iv: " + iv.toHex());
|
||||
|
||||
TRACE("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()));
|
||||
TRACE("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());
|
||||
TRACE("Fernet::encrypt: sig: " + sig.toHex());
|
||||
Bytes token(signed_parts + sig);
|
||||
DEBUG("Fernet::encrypt: token length: " + std::to_string(token.size()));
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
const 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);
|
||||
TRACE("Fernet::decrypt: iv: " + iv.toHex());
|
||||
|
||||
//ciphertext = token[16:-32]
|
||||
Bytes ciphertext = token.mid(16, token.size()-48);
|
||||
TRACE("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()));
|
||||
TRACE("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");
|
||||
}
|
||||
}
|
||||
41
lib/microReticulum/src/Cryptography/Fernet.h
Executable file
41
lib/microReticulum/src/Cryptography/Fernet.h
Executable file
@@ -0,0 +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:
|
||||
using Ptr = std::shared_ptr<Fernet>;
|
||||
|
||||
public:
|
||||
static inline const Bytes generate_key() { return random(32); }
|
||||
|
||||
public:
|
||||
Fernet(const Bytes& key);
|
||||
~Fernet();
|
||||
|
||||
public:
|
||||
bool verify_hmac(const Bytes& token);
|
||||
const Bytes encrypt(const Bytes& data);
|
||||
const Bytes decrypt(const Bytes& token);
|
||||
|
||||
private:
|
||||
Bytes _signing_key;
|
||||
Bytes _encryption_key;
|
||||
};
|
||||
|
||||
} }
|
||||
28
lib/microReticulum/src/Cryptography/HKDF.cpp
Executable file
28
lib/microReticulum/src/Cryptography/HKDF.cpp
Executable file
@@ -0,0 +1,28 @@
|
||||
#include "HKDF.h"
|
||||
|
||||
#include <HKDF.h>
|
||||
#include <SHA256.h>
|
||||
|
||||
using namespace RNS;
|
||||
|
||||
const 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
lib/microReticulum/src/Cryptography/HKDF.h
Executable file
9
lib/microReticulum/src/Cryptography/HKDF.h
Executable file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Bytes.h"
|
||||
|
||||
namespace RNS { namespace Cryptography {
|
||||
|
||||
const Bytes hkdf(size_t length, const Bytes& derive_from, const Bytes& salt = {Bytes::NONE}, const Bytes& context = {Bytes::NONE});
|
||||
|
||||
} }
|
||||
109
lib/microReticulum/src/Cryptography/HMAC.h
Executable file
109
lib/microReticulum/src/Cryptography/HMAC.h
Executable file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Bytes.h"
|
||||
|
||||
#include <Hash.h>
|
||||
#include <SHA256.h>
|
||||
#include <SHA512.h>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
|
||||
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 const 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();
|
||||
}
|
||||
|
||||
} }
|
||||
36
lib/microReticulum/src/Cryptography/Hashes.cpp
Executable file
36
lib/microReticulum/src/Cryptography/Hashes.cpp
Executable file
@@ -0,0 +1,36 @@
|
||||
#include "Hashes.h"
|
||||
|
||||
#include "../Bytes.h"
|
||||
|
||||
#include <SHA256.h>
|
||||
#include <SHA512.h>
|
||||
|
||||
using namespace RNS;
|
||||
|
||||
/*
|
||||
The SHA primitives are abstracted here to allow platform-
|
||||
aware hardware acceleration in the future. Currently only
|
||||
uses Python's internal SHA-256 implementation. All SHA-256
|
||||
calls in RNS end up here.
|
||||
*/
|
||||
|
||||
const Bytes RNS::Cryptography::sha256(const Bytes& data) {
|
||||
//TRACE("Cryptography::sha256: data: " + data.toHex() );
|
||||
SHA256 digest;
|
||||
digest.reset();
|
||||
digest.update(data.data(), data.size());
|
||||
Bytes hash;
|
||||
digest.finalize(hash.writable(32), 32);
|
||||
//TRACE("Cryptography::sha256: hash: " + hash.toHex() );
|
||||
return hash;
|
||||
}
|
||||
|
||||
const Bytes RNS::Cryptography::sha512(const Bytes& data) {
|
||||
SHA512 digest;
|
||||
digest.reset();
|
||||
digest.update(data.data(), data.size());
|
||||
Bytes hash;
|
||||
digest.finalize(hash.writable(64), 64);
|
||||
//TRACE("Cryptography::sha512: hash: " + hash.toHex() );
|
||||
return hash;
|
||||
}
|
||||
12
lib/microReticulum/src/Cryptography/Hashes.h
Executable file
12
lib/microReticulum/src/Cryptography/Hashes.h
Executable file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Bytes.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace RNS { namespace Cryptography {
|
||||
|
||||
const Bytes sha256(const Bytes& data);
|
||||
const Bytes sha512(const Bytes& data);
|
||||
|
||||
} }
|
||||
66
lib/microReticulum/src/Cryptography/PKCS7.h
Executable file
66
lib/microReticulum/src/Cryptography/PKCS7.h
Executable file
@@ -0,0 +1,66 @@
|
||||
#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 const Bytes pad(const Bytes& data, size_t bs = BLOCKSIZE) {
|
||||
Bytes padded(data);
|
||||
inplace_pad(padded, bs);
|
||||
return padded;
|
||||
}
|
||||
|
||||
static inline const 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 zero-filled byte padding array of size padlen
|
||||
//p v = bytes([padlen])
|
||||
//uint8_t pad[padlen] = {0};
|
||||
uint8_t pad[padlen];
|
||||
memset(pad, 0, padlen);
|
||||
// set last byte of padding array to size of padding
|
||||
pad[padlen-1] = (uint8_t)padlen;
|
||||
// concatenate data with padding
|
||||
//p 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));
|
||||
// read last byte which 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");
|
||||
}
|
||||
// truncate data to strip padding
|
||||
//return data[:len-padlen]
|
||||
data.resize(len - padlen);
|
||||
//DEBUG("PKCS7::unpad: data size: " + std::to_string(data.size()));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} }
|
||||
38
lib/microReticulum/src/Cryptography/Random.h
Executable file
38
lib/microReticulum/src/Cryptography/Random.h
Executable file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Bytes.h"
|
||||
|
||||
#include <RNG.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace RNS { namespace Cryptography {
|
||||
|
||||
// return vector specified length of random bytes
|
||||
inline const Bytes random(size_t length) {
|
||||
Bytes rand;
|
||||
RNG.rand(rand.writable(length), length);
|
||||
return rand;
|
||||
}
|
||||
|
||||
// return 32 bit random unigned int
|
||||
inline uint32_t randomnum() {
|
||||
Bytes rand;
|
||||
RNG.rand(rand.writable(4), 4);
|
||||
uint32_t randnum = uint32_t((unsigned char)(rand.data()[0]) << 24 |
|
||||
(unsigned char)(rand.data()[0]) << 16 |
|
||||
(unsigned char)(rand.data()[0]) << 8 |
|
||||
(unsigned char)(rand.data()[0]));
|
||||
return randnum;
|
||||
}
|
||||
|
||||
// return 32 bit random unsigned int between 0 and specified value
|
||||
inline uint32_t randomnum(uint32_t max) {
|
||||
return randomnum() % max;
|
||||
}
|
||||
|
||||
// return random float value from 0 to 1
|
||||
inline float random() {
|
||||
return (float)(randomnum() / (float)0xffffffff);
|
||||
}
|
||||
|
||||
} }
|
||||
159
lib/microReticulum/src/Cryptography/Token.cpp
Executable file
159
lib/microReticulum/src/Cryptography/Token.cpp
Executable file
@@ -0,0 +1,159 @@
|
||||
#include "Token.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;
|
||||
using namespace RNS::Type::Cryptography::Token;
|
||||
|
||||
Token::Token(const Bytes& key, token_mode mode /*= AES*/) {
|
||||
|
||||
if (!key) {
|
||||
throw std::invalid_argument("Token key cannot be None");
|
||||
}
|
||||
|
||||
if (mode == MODE_AES) {
|
||||
if (key.size() == 32) {
|
||||
_mode = MODE_AES_128_CBC;
|
||||
//p self._signing_key = key[:16]
|
||||
_signing_key = key.left(16);
|
||||
//p self._encryption_key = key[16:]
|
||||
_encryption_key = key.mid(16);
|
||||
}
|
||||
else if (key.size() == 64) {
|
||||
_mode = MODE_AES_256_CBC;
|
||||
//p self._signing_key = key[:32]
|
||||
_signing_key = key.left(32);
|
||||
//p self._encryption_key = key[32:]
|
||||
_encryption_key = key.mid(32);
|
||||
}
|
||||
else {
|
||||
throw std::invalid_argument("Token key must be 128 or 256 bits, not " + std::to_string(key.size()*8));
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw std::invalid_argument("Invalid token mode: " + std::to_string(mode));
|
||||
}
|
||||
|
||||
MEM("Token object created");
|
||||
}
|
||||
|
||||
Token::~Token() {
|
||||
MEM("Token object destroyed");
|
||||
}
|
||||
|
||||
bool Token::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("Token::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("Token::verify_hmac: expected_hmac: " + expected_hmac.toHex());
|
||||
|
||||
return (received_hmac == expected_hmac);
|
||||
}
|
||||
|
||||
const Bytes Token::encrypt(const Bytes& data) {
|
||||
|
||||
DEBUG("Token::encrypt: plaintext length: " + std::to_string(data.size()));
|
||||
Bytes iv = random(16);
|
||||
//double current_time = OS::time();
|
||||
TRACE("Token::encrypt: iv: " + iv.toHex());
|
||||
|
||||
TRACE("Token::encrypt: plaintext: " + data.toHex());
|
||||
Bytes ciphertext;
|
||||
if (_mode == MODE_AES_128_CBC) {
|
||||
ciphertext = AES_128_CBC::encrypt(
|
||||
PKCS7::pad(data),
|
||||
_encryption_key,
|
||||
iv
|
||||
);
|
||||
}
|
||||
else if (_mode == MODE_AES_256_CBC) {
|
||||
ciphertext = AES_256_CBC::encrypt(
|
||||
PKCS7::pad(data),
|
||||
_encryption_key,
|
||||
iv
|
||||
);
|
||||
}
|
||||
else {
|
||||
throw new std::invalid_argument("Invalid token mode "+std::to_string(_mode));
|
||||
}
|
||||
DEBUG("Token::encrypt: padded ciphertext length: " + std::to_string(ciphertext.size()));
|
||||
TRACE("Token::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());
|
||||
TRACE("Token::encrypt: sig: " + sig.toHex());
|
||||
Bytes token(signed_parts + sig);
|
||||
DEBUG("Token::encrypt: token length: " + std::to_string(token.size()));
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
const Bytes Token::decrypt(const Bytes& token) {
|
||||
|
||||
DEBUG("Token::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("Token token HMAC was invalid");
|
||||
}
|
||||
|
||||
//iv = token[:16]
|
||||
Bytes iv = token.left(16);
|
||||
TRACE("Token::decrypt: iv: " + iv.toHex());
|
||||
|
||||
//ciphertext = token[16:-32]
|
||||
Bytes ciphertext = token.mid(16, token.size()-48);
|
||||
TRACE("Token::decrypt: ciphertext: " + ciphertext.toHex());
|
||||
|
||||
try {
|
||||
Bytes plaintext;
|
||||
if (_mode == MODE_AES_128_CBC) {
|
||||
plaintext = PKCS7::unpad(
|
||||
AES_128_CBC::decrypt(
|
||||
ciphertext,
|
||||
_encryption_key,
|
||||
iv
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (_mode == MODE_AES_256_CBC) {
|
||||
plaintext = PKCS7::unpad(
|
||||
AES_256_CBC::decrypt(
|
||||
ciphertext,
|
||||
_encryption_key,
|
||||
iv
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
throw new std::invalid_argument("Invalid token mode "+std::to_string(_mode));
|
||||
}
|
||||
DEBUG("Token::encrypt: unpadded plaintext length: " + std::to_string(plaintext.size()));
|
||||
TRACE("Token::decrypt: plaintext: " + plaintext.toHex());
|
||||
|
||||
DEBUG("Token::decrypt: plaintext length: " + std::to_string(plaintext.size()));
|
||||
return plaintext;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
WARNING("Could not decrypt Token token");
|
||||
throw std::runtime_error("Could not decrypt Token token");
|
||||
}
|
||||
}
|
||||
47
lib/microReticulum/src/Cryptography/Token.h
Executable file
47
lib/microReticulum/src/Cryptography/Token.h
Executable file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "Random.h"
|
||||
#include "../Bytes.h"
|
||||
#include "../Type.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 Token {
|
||||
|
||||
public:
|
||||
using Ptr = std::shared_ptr<Token>;
|
||||
|
||||
public:
|
||||
static inline const Bytes generate_key(RNS::Type::Cryptography::Token::token_mode mode = RNS::Type::Cryptography::Token::MODE_AES_256_CBC) {
|
||||
if (mode == RNS::Type::Cryptography::Token::MODE_AES_128_CBC) return random(32);
|
||||
else if (mode == RNS::Type::Cryptography::Token::MODE_AES_256_CBC) return random(64);
|
||||
else throw new std::invalid_argument("Invalid token mode: " + std::to_string(mode));
|
||||
}
|
||||
|
||||
public:
|
||||
Token(const Bytes& key, RNS::Type::Cryptography::Token::token_mode mode = RNS::Type::Cryptography::Token::MODE_AES);
|
||||
~Token();
|
||||
|
||||
public:
|
||||
bool verify_hmac(const Bytes& token);
|
||||
const Bytes encrypt(const Bytes& data);
|
||||
const Bytes decrypt(const Bytes& token);
|
||||
|
||||
private:
|
||||
RNS::Type::Cryptography::Token::token_mode _mode = RNS::Type::Cryptography::Token::MODE_AES_256_CBC;
|
||||
Bytes _signing_key;
|
||||
Bytes _encryption_key;
|
||||
};
|
||||
|
||||
} }
|
||||
3
lib/microReticulum/src/Cryptography/X25519.cpp
Executable file
3
lib/microReticulum/src/Cryptography/X25519.cpp
Executable file
@@ -0,0 +1,3 @@
|
||||
#include "X25519.h"
|
||||
|
||||
using namespace RNS::Cryptography;
|
||||
203
lib/microReticulum/src/Cryptography/X25519.h
Executable file
203
lib/microReticulum/src/Cryptography/X25519.h
Executable file
@@ -0,0 +1,203 @@
|
||||
#pragma once
|
||||
|
||||
#include "Bytes.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <Curve25519.h>
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace RNS { namespace Cryptography {
|
||||
|
||||
class X25519PublicKey {
|
||||
|
||||
public:
|
||||
using Ptr = std::shared_ptr<X25519PublicKey>;
|
||||
|
||||
public:
|
||||
/*
|
||||
X25519PublicKey(const Bytes& x) {
|
||||
_x = x;
|
||||
}
|
||||
*/
|
||||
X25519PublicKey(const Bytes& publicKey) {
|
||||
_publicKey = publicKey;
|
||||
}
|
||||
~X25519PublicKey() {}
|
||||
|
||||
public:
|
||||
// creates a new instance with specified seed
|
||||
/*
|
||||
static inline Ptr from_public_bytes(const Bytes& data) {
|
||||
return Ptr(new X25519PublicKey(_unpack_number(data)));
|
||||
}
|
||||
*/
|
||||
static inline Ptr from_public_bytes(const Bytes& publicKey) {
|
||||
return Ptr(new X25519PublicKey(publicKey));
|
||||
}
|
||||
|
||||
/*
|
||||
Bytes public_bytes() {
|
||||
return _pack_number(_x);
|
||||
}
|
||||
*/
|
||||
Bytes public_bytes() {
|
||||
return _publicKey;
|
||||
}
|
||||
|
||||
private:
|
||||
//Bytes _x;
|
||||
Bytes _publicKey;
|
||||
|
||||
};
|
||||
|
||||
class X25519PrivateKey {
|
||||
|
||||
public:
|
||||
const float MIN_EXEC_TIME = 2; // in milliseconds
|
||||
const float MAX_EXEC_TIME = 500; // in milliseconds
|
||||
const uint8_t DELAY_WINDOW = 10;
|
||||
|
||||
//z T_CLEAR = None
|
||||
const uint8_t T_MAX = 0;
|
||||
|
||||
using Ptr = std::shared_ptr<X25519PrivateKey>;
|
||||
|
||||
public:
|
||||
/*
|
||||
X25519PrivateKey(const Bytes& a) {
|
||||
_a = a;
|
||||
}
|
||||
*/
|
||||
X25519PrivateKey(const Bytes& privateKey) {
|
||||
if (privateKey) {
|
||||
// use specified private key
|
||||
_privateKey = privateKey;
|
||||
// similar to derive public key from private key
|
||||
// second param "f" is secret
|
||||
//eval(uint8_t result[32], const uint8_t s[32], const uint8_t x[32])
|
||||
// derive public key from private key
|
||||
Curve25519::eval(_publicKey.writable(32), _privateKey.data(), 0);
|
||||
}
|
||||
else {
|
||||
// create random private key and derive public key
|
||||
// second param "f" is secret
|
||||
//dh1(uint8_t k[32], uint8_t f[32])
|
||||
Curve25519::dh1(_publicKey.writable(32), _privateKey.writable(32));
|
||||
}
|
||||
}
|
||||
~X25519PrivateKey() {}
|
||||
|
||||
public:
|
||||
// creates a new instance with a random seed
|
||||
/*
|
||||
static inline Ptr generate() {
|
||||
return from_private_bytes(os.urandom(32));
|
||||
}
|
||||
*/
|
||||
static inline Ptr generate() {
|
||||
return from_private_bytes({Bytes::NONE});
|
||||
}
|
||||
|
||||
// creates a new instance with specified seed
|
||||
/*
|
||||
static inline Ptr from_private_bytes(const Bytes& data) {
|
||||
return Ptr(new X25519PrivateKey(_fix_secret(_unpack_number(data))));
|
||||
}
|
||||
*/
|
||||
static inline Ptr from_private_bytes(const Bytes& privateKey) {
|
||||
return Ptr(new X25519PrivateKey(privateKey));
|
||||
}
|
||||
|
||||
/*
|
||||
inline const Bytes private_bytes() {
|
||||
return _pack_number(_a);
|
||||
}
|
||||
*/
|
||||
inline const Bytes& private_bytes() {
|
||||
return _privateKey;
|
||||
}
|
||||
|
||||
// creates a new instance of public key for this private key
|
||||
/*
|
||||
inline X25519PublicKey::Ptr public_key() {
|
||||
return X25519PublicKey::from_public_bytes(_pack_number(_raw_curve25519(9, _a)));
|
||||
}
|
||||
*/
|
||||
inline X25519PublicKey::Ptr public_key() {
|
||||
return X25519PublicKey::from_public_bytes(_publicKey);
|
||||
}
|
||||
|
||||
/*
|
||||
inline const Bytes exchange(const Bytes& peer_public_key) {
|
||||
if isinstance(peer_public_key, bytes):
|
||||
peer_public_key = X25519PublicKey.from_public_bytes(peer_public_key)
|
||||
|
||||
start = OS::time()
|
||||
|
||||
shared = _pack_number(_raw_curve25519(peer_public_key.x, _a))
|
||||
|
||||
end = OS::time()
|
||||
duration = end-start
|
||||
|
||||
if X25519PrivateKey.T_CLEAR == None:
|
||||
X25519PrivateKey.T_CLEAR = end + X25519PrivateKey.DELAY_WINDOW
|
||||
|
||||
if end > X25519PrivateKey.T_CLEAR:
|
||||
X25519PrivateKey.T_CLEAR = end + X25519PrivateKey.DELAY_WINDOW
|
||||
X25519PrivateKey.T_MAX = 0
|
||||
|
||||
if duration < X25519PrivateKey.T_MAX or duration < X25519PrivateKey.MIN_EXEC_TIME:
|
||||
target = start+X25519PrivateKey.T_MAX
|
||||
|
||||
if target > start+X25519PrivateKey.MAX_EXEC_TIME:
|
||||
target = start+X25519PrivateKey.MAX_EXEC_TIME
|
||||
|
||||
if target < start+X25519PrivateKey.MIN_EXEC_TIME:
|
||||
target = start+X25519PrivateKey.MIN_EXEC_TIME
|
||||
|
||||
try:
|
||||
OS::sleep(target-OS::time())
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
elif duration > X25519PrivateKey.T_MAX:
|
||||
X25519PrivateKey.T_MAX = duration
|
||||
|
||||
return shared
|
||||
}
|
||||
*/
|
||||
inline const Bytes exchange(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;
|
||||
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;
|
||||
Bytes _publicKey;
|
||||
|
||||
};
|
||||
|
||||
} }
|
||||
Reference in New Issue
Block a user