libcryfs/src/cpp-utils/crypto/symmetric/testutils/FakeAuthenticatedCipher.h

122 lines
4.4 KiB
C++

#pragma once
#ifndef MESSMER_CPPUTILS_TEST_CRYPTO_SYMMETRIC_TESTUTILS_FAKEAUTHENTICATEDCIPHER_H_
#define MESSMER_CPPUTILS_TEST_CRYPTO_SYMMETRIC_TESTUTILS_FAKEAUTHENTICATEDCIPHER_H_
#include "cpp-utils/crypto/symmetric/Cipher.h"
#include "cpp-utils/data/FixedSizeData.h"
#include "cpp-utils/data/Data.h"
#include "cpp-utils/random/RandomGenerator.h"
#include <random>
#include <cpp-utils/data/SerializationHelper.h>
namespace cpputils {
struct FakeKey {
static FakeKey FromString(const std::string& keyData) {
return FakeKey{static_cast<uint64_t>(std::strtol(keyData.c_str(), nullptr, 10))};
}
size_t binaryLength() const {
return sizeof(uint64_t);
}
static FakeKey CreateKey(RandomGenerator &randomGenerator, size_t keySize) {
ASSERT(keySize == sizeof(uint64_t), "Wrong key size");
auto data = randomGenerator.getFixedSize<sizeof(uint64_t)>();
return FakeKey {*reinterpret_cast<uint64_t*>(data.data())};
}
uint64_t value;
};
// This is a fake cipher that uses an indeterministic xor chiffre and a 8-byte checksum for a simple authentication mechanism
class FakeAuthenticatedCipher {
public:
BOOST_CONCEPT_ASSERT((CipherConcept<FakeAuthenticatedCipher>));
using EncryptionKey = FakeKey;
static constexpr unsigned int KEYSIZE = sizeof(uint64_t);
static constexpr unsigned int STRING_KEYSIZE = 2 * KEYSIZE;
static EncryptionKey Key1() {
return FakeKey{5};
}
static EncryptionKey Key2() {
return FakeKey{63};
}
static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) {
return plaintextBlockSize + sizeof(uint64_t) + sizeof(uint64_t);
}
static constexpr unsigned int plaintextSize(unsigned int ciphertextBlockSize) {
return ciphertextBlockSize - sizeof(uint64_t) - sizeof(uint64_t);
}
static Data encrypt(const CryptoPP::byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) {
Data result(ciphertextSize(plaintextSize));
//Add a random IV
uint64_t iv = std::uniform_int_distribution<uint64_t>()(random_);
serialize<uint64_t>(result.data(), iv);
//Use xor chiffre on plaintext
_xor(static_cast<CryptoPP::byte*>(result.dataOffset(sizeof(uint64_t))), plaintext, plaintextSize, encKey.value ^ iv);
//Add checksum information
uint64_t checksum = _checksum(static_cast<const CryptoPP::byte*>(result.data()), encKey, plaintextSize + sizeof(uint64_t));
serialize<uint64_t>(result.dataOffset(plaintextSize + sizeof(uint64_t)), checksum);
return result;
}
static boost::optional <Data> decrypt(const CryptoPP::byte *ciphertext, unsigned int ciphertextSize,
const EncryptionKey &encKey) {
//We need at least 16 bytes (iv + checksum)
if (ciphertextSize < 16) {
return boost::none;
}
//Check checksum
uint64_t expectedParity = _checksum(ciphertext, encKey, plaintextSize(ciphertextSize) + sizeof(uint64_t));
uint64_t actualParity = deserialize<uint64_t>(ciphertext + plaintextSize(ciphertextSize) + sizeof(uint64_t));
if (expectedParity != actualParity) {
return boost::none;
}
//Decrypt xor chiffre from ciphertext
uint64_t iv = deserialize<uint64_t>(ciphertext);
Data result(plaintextSize(ciphertextSize));
_xor(static_cast<CryptoPP::byte *>(result.data()), ciphertext + sizeof(uint64_t), plaintextSize(ciphertextSize), encKey.value ^ iv);
return result;
}
static constexpr const char *NAME = "FakeAuthenticatedCipher";
private:
static uint64_t _checksum(const CryptoPP::byte *data, FakeKey encKey, std::size_t size) {
uint64_t checksum = 34343435 * encKey.value; // some init value
for (size_t i = 0; i < size; ++i) {
checksum ^= (static_cast<uint64_t>(data[i]) << (56 - 8 * (i%8)));
}
return checksum;
}
static void _xor(CryptoPP::byte *dst, const CryptoPP::byte *src, unsigned int size, uint64_t key) {
for (unsigned int i = 0; i < size; ++i) {
dst[i] = src[i] ^ ((key >> (56 - 8*(i%8))) & 0xFF);
}
}
static std::random_device random_;
};
}
#endif