AFV initial commit

This commit is contained in:
Roland Rossgotterer
2019-09-14 21:18:26 +02:00
committed by Mat Sutcliffe
parent 7030302e73
commit b5a2f2ad13
100 changed files with 6821 additions and 25 deletions

View File

@@ -0,0 +1,128 @@
#include "cryptodtochannel.h"
CryptoDtoChannel::CryptoDtoChannel(QString channelTag, const QByteArray &aeadReceiveKey, const QByteArray &aeadTransmitKey, int receiveSequenceHistorySize)
{
ChannelTag = channelTag;
m_aeadReceiveKey = aeadReceiveKey;
m_aeadTransmitKey = aeadTransmitKey;
receiveSequenceSizeMaxSize = receiveSequenceHistorySize;
if (receiveSequenceSizeMaxSize < 1)
receiveSequenceSizeMaxSize = 1;
receiveSequenceHistory = new uint[receiveSequenceSizeMaxSize];
receiveSequenceHistoryDepth = 0;
}
CryptoDtoChannel::CryptoDtoChannel(CryptoDtoChannelConfigDto channelConfig, int receiveSequenceHistorySize)
{
ChannelTag = channelConfig.channelTag;
m_aeadReceiveKey = channelConfig.aeadReceiveKey;
m_aeadTransmitKey = channelConfig.aeadTransmitKey;
hmacKey = channelConfig.hmacKey;
receiveSequenceSizeMaxSize = receiveSequenceHistorySize;
if (receiveSequenceSizeMaxSize < 1)
receiveSequenceSizeMaxSize = 1;
receiveSequenceHistory = new uint[receiveSequenceSizeMaxSize];
receiveSequenceHistoryDepth = 0;
}
QByteArray CryptoDtoChannel::getTransmitKey(CryptoDtoMode mode)
{
switch (mode)
{
case CryptoDtoMode::AEAD_ChaCha20Poly1305: return m_aeadTransmitKey;
case CryptoDtoMode::Undefined:
case CryptoDtoMode::None:
qFatal("GetTransmitKey called with wrong argument.");
}
return {};
}
QByteArray CryptoDtoChannel::getTransmitKey(CryptoDtoMode mode, uint &sequenceToSend)
{
sequenceToSend = transmitSequence;
transmitSequence++;
LastTransmitUtc = QDateTime::currentDateTimeUtc();
switch (mode)
{
case CryptoDtoMode::AEAD_ChaCha20Poly1305: return m_aeadTransmitKey;
case CryptoDtoMode::Undefined:
case CryptoDtoMode::None:
qFatal("GetTransmitKey called with wrong argument.");
}
return {};
}
QString CryptoDtoChannel::getChannelTag() const
{
return ChannelTag;
}
QByteArray CryptoDtoChannel::getReceiveKey(CryptoDtoMode mode)
{
switch (mode)
{
case CryptoDtoMode::AEAD_ChaCha20Poly1305: return m_aeadReceiveKey;
case CryptoDtoMode::Undefined:
case CryptoDtoMode::None:
qFatal("getReceiveKey called with wrong argument.");
}
return {};
}
bool CryptoDtoChannel::checkReceivedSequence(uint sequenceReceived)
{
if (contains(sequenceReceived))
{
// Duplication or replay attack
return false;
}
if (receiveSequenceHistoryDepth < receiveSequenceSizeMaxSize) //If the buffer has been filled...
{
receiveSequenceHistory[receiveSequenceHistoryDepth++] = sequenceReceived;
}
else
{
int minIndex;
uint minValue = getMin(minIndex);
if (sequenceReceived < minValue) { return false; } // Possible replay attack
receiveSequenceHistory[minIndex] = sequenceReceived;
}
LastReceiveUtc = QDateTime::currentDateTimeUtc();
return true;
}
bool CryptoDtoChannel::contains(uint sequence)
{
for (int i = 0; i < receiveSequenceHistoryDepth; i++)
{
if (receiveSequenceHistory[i] == sequence)
return true;
}
return false;
}
uint CryptoDtoChannel::getMin(int &minIndex)
{
uint minValue = std::numeric_limits<uint>::max();
minIndex = -1;
int index = -1;
for (int i = 0; i < receiveSequenceHistoryDepth; i++)
{
index++;
if (receiveSequenceHistory[i] <= minValue)
{
minValue = receiveSequenceHistory[i];
minIndex = index;
}
}
return minValue;
}

View File

@@ -0,0 +1,47 @@
#ifndef CRYPTODTOCHANNEL_H
#define CRYPTODTOCHANNEL_H
#include "blackcore/afv/dto.h"
#include "cryptodtomode.h"
#include <QDateTime>
#include <QByteArray>
#include <limits>
class CryptoDtoChannel
{
public:
CryptoDtoChannel(QString channelTag, const QByteArray &aeadReceiveKey, const QByteArray &aeadTransmitKey, int receiveSequenceHistorySize = 10);
CryptoDtoChannel(CryptoDtoChannelConfigDto channelConfig, int receiveSequenceHistorySize = 10);
QByteArray getTransmitKey(CryptoDtoMode mode);
QByteArray getTransmitKey(CryptoDtoMode mode, uint &sequenceToSend);
QString getChannelTag() const;
QByteArray getReceiveKey(CryptoDtoMode mode);
bool checkReceivedSequence(uint sequenceReceived);
private:
bool contains(uint sequence);
uint getMin(int &minIndex);
QByteArray m_aeadTransmitKey;
uint transmitSequence = 0;
QByteArray m_aeadReceiveKey;
uint *receiveSequenceHistory;
int receiveSequenceHistoryDepth;
int receiveSequenceSizeMaxSize;
QByteArray hmacKey;
QString ChannelTag;
QDateTime LastTransmitUtc;
QDateTime LastReceiveUtc;
};
#endif // CRYPTODTOCHANNEL_H

View File

@@ -0,0 +1,17 @@
#ifndef CRYPTODTOHEADERDTO_H
#define CRYPTODTOHEADERDTO_H
#include "cryptodtomode.h"
#include "msgpack.hpp"
#include <QString>
#include <cstdint>
struct CryptoDtoHeaderDto
{
std::string ChannelTag;
uint64_t Sequence;
CryptoDtoMode Mode;
MSGPACK_DEFINE(ChannelTag, Sequence, Mode)
};
#endif // CRYPTODTOHEADERDTO_H

View File

@@ -0,0 +1,15 @@
#ifndef CRYPTODTOMODE_H
#define CRYPTODTOMODE_H
#include "msgpack.hpp"
enum class CryptoDtoMode
{
Undefined = 0,
None = 1,
AEAD_ChaCha20Poly1305 = 2
};
MSGPACK_ADD_ENUM(CryptoDtoMode);
#endif // CRYPTODTOMODE_H

View File

@@ -0,0 +1,72 @@
#include "cryptodtoserializer.h"
CryptoDtoSerializer::CryptoDtoSerializer()
{
}
CryptoDtoSerializer::Deserializer CryptoDtoSerializer::deserialize(CryptoDtoChannel &channel, const QByteArray &bytes, bool loopback)
{
return Deserializer(channel, bytes, loopback);
}
CryptoDtoSerializer::Deserializer::Deserializer(CryptoDtoChannel &channel, const QByteArray &bytes, bool loopback)
{
QByteArray data(bytes);
QBuffer buffer(&data);
buffer.open(QIODevice::ReadOnly);
buffer.read((char *)&headerLength, sizeof(headerLength));
QByteArray headerBuffer = buffer.read(headerLength);
msgpack::object_handle oh = msgpack::unpack(headerBuffer.data(), headerBuffer.size());
header = oh.get().as<CryptoDtoHeaderDto>();
if(header.Mode == CryptoDtoMode::AEAD_ChaCha20Poly1305)
{
int aeLength = buffer.size() - (2 + headerLength);
const QByteArray aePayloadBuffer = buffer.read(aeLength);
const QByteArray adBuffer = data.left(2 + headerLength);
QByteArray nonce;
nonce.fill(0, crypto_aead_chacha20poly1305_IETF_NPUBBYTES);
QBuffer nonceBuffer(&nonce);
nonceBuffer.open(QIODevice::WriteOnly);
uint32_t id = 0;
nonceBuffer.write(reinterpret_cast<const char *>(&id), sizeof(id));
nonceBuffer.write(reinterpret_cast<const char *>(&header.Sequence), sizeof(header.Sequence));
nonceBuffer.close();
QByteArray decryptedPayload;
unsigned long long mlen = 500;
decryptedPayload.fill(0, mlen);
QByteArray key;
if (loopback) { key = channel.getTransmitKey(CryptoDtoMode::AEAD_ChaCha20Poly1305); }
else { key = channel.getReceiveKey(CryptoDtoMode::AEAD_ChaCha20Poly1305); }
int result = crypto_aead_chacha20poly1305_ietf_decrypt(reinterpret_cast<unsigned char *>(decryptedPayload.data()), &mlen, nullptr,
reinterpret_cast<const unsigned char *>(aePayloadBuffer.constData()), aePayloadBuffer.size(),
reinterpret_cast<const unsigned char *>(adBuffer.constData()), adBuffer.size(),
reinterpret_cast<const unsigned char *>(nonce.constData()),
reinterpret_cast<const unsigned char *>(key.constData()));
if (result == 0)
{
decryptedPayload.resize(mlen);
// Fix this:
// if (! channel.checkReceivedSequence(header.Sequence)) { }
QBuffer decryptedPayloadBuffer(&decryptedPayload);
decryptedPayloadBuffer.open(QIODevice::ReadOnly);
decryptedPayloadBuffer.read((char *)&dtoNameLength, sizeof(dtoNameLength));
dtoNameBuffer = decryptedPayloadBuffer.read(dtoNameLength);
decryptedPayloadBuffer.read((char *)&dataLength, sizeof(dataLength));
dataBuffer = decryptedPayloadBuffer.read(dataLength);
verified = true;
}
}
}

View File

@@ -0,0 +1,132 @@
#ifndef CRYPTODTOSERIALIZER_H
#define CRYPTODTOSERIALIZER_H
#include "cryptodtochannel.h"
#include "cryptodtomode.h"
#include "cryptodtoheaderdto.h"
#include "sodium.h"
#include <QByteArray>
#include <QBuffer>
#include <QtDebug>
extern QHash<QByteArray, QByteArray> gShortDtoNames;
class CryptoDtoSerializer
{
public:
CryptoDtoSerializer();
template<typename T>
static QByteArray Serialize(const QString &channelTag, CryptoDtoMode mode, const QByteArray &transmitKey, uint sequenceToBeSent, T dto)
{
const CryptoDtoHeaderDto header = { channelTag.toStdString(), sequenceToBeSent, mode };
QBuffer headerBuffer;
headerBuffer.open(QIODevice::WriteOnly);
msgpack::pack(headerBuffer, header);
headerBuffer.close();
const quint16 headerLength = static_cast<quint16>(headerBuffer.buffer().size());
const QByteArray dtoNameBuffer = T::getDtoName();
const QByteArray dtoShortName = T::getShortDtoName();
const quint16 dtoNameLength = static_cast<quint16>(dtoShortName.size());
QBuffer dtoBuffer;
dtoBuffer.open(QIODevice::WriteOnly);
msgpack::pack(dtoBuffer, dto);
dtoBuffer.close();
const quint16 dtoLength = static_cast<quint16>(dtoBuffer.buffer().size());
if(header.Mode == CryptoDtoMode::AEAD_ChaCha20Poly1305)
{
QBuffer aePayloadBuffer;
aePayloadBuffer.open(QIODevice::WriteOnly);
aePayloadBuffer.write(reinterpret_cast<const char *>(&dtoNameLength), sizeof(dtoNameLength));
aePayloadBuffer.write(dtoShortName);
aePayloadBuffer.write(reinterpret_cast<const char *>(&dtoLength), sizeof(dtoLength));
aePayloadBuffer.write(dtoBuffer.buffer());
aePayloadBuffer.close();
QBuffer adPayloadBuffer;
adPayloadBuffer.open(QIODevice::WriteOnly);
adPayloadBuffer.write(reinterpret_cast<const char *>(&headerLength), sizeof(headerLength));
adPayloadBuffer.write(headerBuffer.buffer());
adPayloadBuffer.close();
QByteArray nonce;
nonce.fill(0, crypto_aead_chacha20poly1305_IETF_NPUBBYTES);
QBuffer nonceBuffer(&nonce);
nonceBuffer.open(QIODevice::WriteOnly);
uint32_t id = 0;
nonceBuffer.write(reinterpret_cast<const char *>(&id), sizeof(id));
nonceBuffer.write(reinterpret_cast<const char *>(&header.Sequence), sizeof(header.Sequence));
nonceBuffer.close();
unsigned long long clen;
QByteArray aeadPayload;
aeadPayload.fill(0, static_cast<int>(aePayloadBuffer.size() + crypto_aead_chacha20poly1305_IETF_ABYTES));
int result = crypto_aead_chacha20poly1305_ietf_encrypt(reinterpret_cast<unsigned char*>(aeadPayload.data()),
&clen,
reinterpret_cast<const unsigned char*>(aePayloadBuffer.buffer().constData()), aePayloadBuffer.size(),
reinterpret_cast<const unsigned char*>(adPayloadBuffer.buffer().constData()), adPayloadBuffer.size(),
nullptr,
reinterpret_cast<const unsigned char*>(nonce.constData()),
reinterpret_cast<const unsigned char*>(transmitKey.constData()));
if (result != 0) { return {}; }
QBuffer packetBuffer;
packetBuffer.open(QIODevice::WriteOnly);
packetBuffer.write(reinterpret_cast<const char *>(&headerLength), sizeof(headerLength));
packetBuffer.write(headerBuffer.buffer());
packetBuffer.write(aeadPayload);
packetBuffer.close();
return packetBuffer.buffer();
}
return {};
}
template<typename T>
static QByteArray Serialize(CryptoDtoChannel &channel, CryptoDtoMode mode, T dto)
{
uint sequenceToSend = 0;
QByteArray transmitKey = channel.getTransmitKey(mode, sequenceToSend);
return Serialize(channel.getChannelTag(), mode, transmitKey, sequenceToSend++, dto);
}
struct Deserializer
{
Deserializer(CryptoDtoChannel &channel, const QByteArray &bytes, bool loopback);
template<typename T>
T getDto()
{
if (! verified) return {};
if (dtoNameBuffer == T::getDtoName() || dtoNameBuffer == T::getShortDtoName())
{
msgpack::object_handle oh2 = msgpack::unpack(dataBuffer.data(), dataBuffer.size());
msgpack::object obj = oh2.get();
T dto = obj.as<T>();
return dto;
}
return {};
}
quint16 headerLength;
CryptoDtoHeaderDto header;
quint16 dtoNameLength;
QByteArray dtoNameBuffer;
quint16 dataLength;
QByteArray dataBuffer;
bool verified = false;
};
static Deserializer deserialize(CryptoDtoChannel &channel, const QByteArray &bytes, bool loopback);
};
#endif // CRYPTODTOSERIALIZER_H