Added AES implementation

- Uses reference to Galois.h
  - Essentially a translated copy of my ServerProject (C#) implementation
  - There are almost no comments. Sorry. I'll add them later
Added some support functions to Galois
Added AES dependency to test project
Changed includes to be independent from filesystem naming (changed from absolute paths to macros)
This commit is contained in:
Gabriel Tofvesson 2018-03-05 06:27:26 +01:00
parent 6cbecfd2c6
commit 06620d4d7f
8 changed files with 433 additions and 2 deletions

301
AES/AES.cpp Normal file
View File

@ -0,0 +1,301 @@
#define AES_API
#include "AES.h"
#include "Galois.h"
#include <string>
namespace CryptoCPP { namespace Crypto {
AES_API AES::Digest::Digest(const char * message, const size_t size) : message(message), size(size)
{ }
AES_API AES::Digest::Digest(const char * message) : message(message), size(strlen(message))
{ }
AES_API AES::AES(const char * key, KeySize bitsize) : size(bitsize)
{
this->key = expand_key(key, bitsize);
}
AES_API AES::AES(const AES & copy) : size(copy.size)
{
this->key = new char[size == AES128 ? 16 : size == AES192 ? 24 : 32];
memcpy(key, copy.key, size == AES128 ? 16 : size == AES192 ? 24 : 32);
}
AES_API AES::AES(char * key, KeySize size) : size(size)
{
this->key = key;
}
AES_API AES::Digest * AES::encrypt(Digest d)
{
// Allocate states
size_t state_count = (d.size / 16) + (d.size % 16 == 0 ? 0 : 1);
char * states = new char[state_count * 16];
// Set up state properly
memset(states + d.size, 0, (state_count * 16) - d.size);
memcpy(states, d.message, d.size);
// Encrypt states
for (size_t t = 0; t < state_count; ++t) encrypt(states + (t * 16));
return new Digest(states, state_count * 16);
}
AES_API AES::Digest * AES::decrypt(Digest d)
{
// Allocate states
size_t state_count = d.size / 16;
char * states = new char[state_count * 16];
// Set up state properly
memcpy(states, d.message, d.size);
// Encrypt states
for (size_t t = 0; t < state_count; ++t) decrypt(states + (t * 16));
return new Digest(states, d.size);
}
AES_API AES::Serialized* AES::serialize()
{
AES::Serialized * s = new AES::Serialized();
s->data_size = size == AES128 ? 16 : size == AES192 ? 24 : 32;
s->data = new char[1 + s->data_size];
memcpy(s->data + 1, key, s->data_size);
s->data[0] = size == AES128 ? 0 : size == AES192 ? 1 : 2;
return s;
}
AES_API AES * AES::deserialize(char* data)
{
KeySize size = data[0] == 0 ? AES128 : data[0] == 1 ? AES192 : AES256;
char * key = new char[size == AES128 ? 16 : size == AES192 ? 24 : 32];
return new AES(key, size);
}
AES_API AES * AES::deserialize(AES::Serialized* ser)
{
return deserialize(ser->data);
}
AES_API void AES::encrypt(char* state)
{
size_t last_round = size == AES128 ? 9 : size == AES192 ? 11 : 13;
// Initial round. Just just xor the key for this round input the input
add_round_key(state, key);
// Rounds 1 - last
for (size_t rounds = 1; rounds < 10; ++rounds)
{
sub_bytes(state);
shift_rows(state);
if (rounds != last_round) mix_columns(state);
add_round_key(state, key + (rounds * 16));
}
}
AES_API void AES::decrypt(char* state)
{
size_t last_round = size == AES128 ? 9 : size == AES192 ? 11 : 13;
for (int rounds = last_round; rounds > 0; --rounds)
{
inv_add_round_key(state, key + (rounds * 16));
if (rounds != last_round) inv_mix_columns(state);
inv_shift_rows(state);
inv_sub_bytes(state);
}
inv_add_round_key(state, key);
}
// Key expansion
AES_API unsigned int AES::key_schedule_core(unsigned int input, size_t rcon_iteration)
{
char * convert = (char*)&input;
for (size_t t = 0; t < 4; ++t)
convert[t] = sbox(convert[t]);
convert[3] ^= rcon(rcon_iteration);
return input;
}
AES_API char* AES::expand_key(const char* key, KeySize size)
{
size_t n = size == AES128 ? 16 : size == AES192 ? 24 : 32;
size_t b = size == AES128 ? 176 : size == AES192 ? 208 : 240;
char* output = new char[b];
//memset(output + n, 0, b - n);
memcpy(output, key, n);
size_t rcon_iter = 1;
size_t accruedBytes = n;
while (accruedBytes < b)
{
// Generate 4 new bytes of extended key
unsigned int _t = key_schedule_core(*(unsigned int *)(output + accruedBytes - 4), rcon_iter);
char * t = (char*)&_t;
++rcon_iter;
for (size_t i = 0; i < 4; ++i) t[i] ^= output[accruedBytes - n + i];
memcpy(output + accruedBytes, t, 4);
accruedBytes += 4;
// Generate 12 new bytes of extended key
for (int i = 0; i < 3; ++i)
{
memcpy(t, output + accruedBytes - 4, 4);
for (size_t j = 0; j < 4; ++j) t[j] ^= output[accruedBytes - n + j];
memcpy(output + accruedBytes, t, 4);
accruedBytes += 4;
}
// Special processing for 256-bit key schedule
if (size == AES256)
{
memcpy(t, output + accruedBytes - 4, 4);
for (size_t j = 0; j < 4; ++j) t[j] = (sbox(t[j]) ^ output[accruedBytes - n + j]);
memcpy(output + accruedBytes, t, 4);
accruedBytes += 4;
}
// Special processing for 192-bit key schedule
if (size != AES128)
for (int i = size == AES192 ? 1 : 2; i >= 0; --i)
{
memcpy(t, output + accruedBytes - 4, 4);
for (int j = 0; j < 4; ++j) t[j] ^= output[accruedBytes - n + j];
memcpy(output + accruedBytes, t, 4);
accruedBytes += 4;
}
}
return output;
}
AES_API char AES::rcon(unsigned int iteration)
{
return iteration == 0 ? 0x8d : Math::Galois(2, 283, iteration).get_lowest() & 255;
}
AES_API char AES::sbox(char c)
{
Math::Galois * inv = Math::Galois(2, 283, c).inv();
c = affine(inv->get_lowest() & 255);
delete inv;
return c;
}
// Encryption functions
AES_API void AES::add_round_key(char* state, char* roundKey)
{
for (size_t t = 0; t < 16; ++t) state[t] ^= roundKey[t];
}
AES_API void AES::mix_columns(char* state)
{
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
for (int k = 0; k < 4; ++k)
{
Math::Galois g = Math::Galois(2, 283, state[k + i * 4]);
Math::Galois mix = Math::Galois(2, 283, mix_matrix[(k + 4 - j) % 4]);
Math::Galois * result = g.mul(&mix);
state[j + i * 4] ^= result->get_lowest() & 255;
delete result;
}
}
AES_API void AES::shift_rows(char* state)
{
for (size_t i = 1; i < 4; ++i)
{
unsigned int value = get_row(state, i);
for (size_t j = 0; j < i; ++j) value = rotate(value);
write_to_row(value, state, i);
}
}
AES_API void AES::sub_bytes(char* state)
{
for (size_t t = 0; t < 16; ++t)
state[t] = sbox(state[t]);
}
AES_API char AES::affine(char value)
{
return (value ^ rot(value, 1) ^ rot(value, 2) ^ rot(value, 3) ^ rot(value, 4) ^ 0b01100011);
}
// Inverse encryption functions
AES_API void AES::inv_add_round_key(char* state, char* roundKey)
{
add_round_key(state, roundKey);
}
AES_API void AES::inv_mix_columns(char* state)
{
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
for (int k = 0; k < 4; ++k)
{
Math::Galois g = Math::Galois(2, 283, state[k + i * 4]);
Math::Galois mix = Math::Galois(2, 283, unmix_matrix[(k + 4 - j) % 4]);
Math::Galois * result = g.mul(&mix);
state[j + i * 4] ^= result->get_lowest() & 255;
delete result;
}
}
AES_API void AES::inv_shift_rows(char* state)
{
for (size_t i = 1; i < 4; ++i)
{
unsigned int value = get_row(state, i);
for (size_t j = 3; j >= i; --j) value = rotate(value);
write_to_row(value, state, i);
}
}
AES_API void AES::inv_sub_bytes(char* state)
{
for (size_t t = 0; t < 16; ++t)
{
Math::Galois * inv = Math::Galois(2, 283, inv_affine(state[t])).inv();
state[t] = inv->get_lowest() & 255;
delete inv;
}
}
AES_API char AES::inv_affine(char value)
{
return rot(value, 1) ^ rot(value, 3) ^ rot(value, 6) ^ 0b00000101;
}
// Helper functions
AES_API void AES::write_to_row(unsigned int value, char* to, size_t row)
{
to[row] = value & 255;
to[row + 4] = (value >> 8) & 255;
to[row + 8] = (value >> 16) & 255;
to[row + 12] = (value >> 24) & 255;
}
AES_API unsigned int AES::rotate(unsigned int i)
{
return (i >> 24) | (i << 8);
}
AES_API unsigned int AES::get_row(char* from, size_t row)
{
return (from[row] | (from[row + 4] << 8) | (from[row + 8] << 16) | (from[row + 12] << 24));
}
AES_API char AES::rot(char value, size_t by)
{
return (value << by) | (value >> (8 - by));
}
}}

85
AES/AES.h Normal file
View File

@ -0,0 +1,85 @@
#pragma once
#if defined(__MINGW32__) || defined(_WIN32)
#if defined(AES_API)
#undef AES_API
#define AES_API __declspec(dllexport)
#else
#define AES_API __declspec(dllimport)
#endif
#endif
#ifndef AES_API
#if __GNUC__ >= 4
#define AES_API __attribute__ ((visibility ("default")))
#else
#define AES_API
#endif
#endif
namespace CryptoCPP { namespace Crypto {
// MixColumns matrix basis
static const char mix_matrix[4] = { 2, 3, 1, 1 };
static const char unmix_matrix[4] = { 14, 11, 13, 9 };
class AES {
public:
struct Digest {
AES_API Digest(const char * message, const size_t size);
AES_API Digest(const char * message);
const char* message;
const size_t size;
};
enum KeySize{ AES128, AES192, AES256 };
AES_API AES(const char * key, KeySize bitsize = AES128);
AES_API AES(const AES & copy);
AES_API Digest * encrypt(Digest d);
AES_API Digest * decrypt(Digest d);
// Serialization
struct Serialized {
char* data;
size_t data_size;
};
AES_API Serialized* serialize();
AES_API static AES * deserialize(char* data);
AES_API static AES * deserialize(Serialized* ser);
protected:
char* key;
const KeySize size;
AES_API AES(char * key, KeySize size);
AES_API void encrypt(char* state);
AES_API void decrypt(char* state);
AES_API static unsigned int key_schedule_core(unsigned int input, size_t rcon_iteration);
AES_API static char* expand_key(const char* key, KeySize size);
AES_API static char rcon(unsigned int iteration);
AES_API static char sbox(char c);
AES_API static void add_round_key(char* state, char* roundKey);
AES_API static void mix_columns(char* state);
AES_API static void shift_rows(char* state);
AES_API static void sub_bytes(char* state);
AES_API static char affine(char value);
AES_API static void inv_add_round_key(char* state, char* roundKey);
AES_API static void inv_mix_columns(char* state);
AES_API static void inv_shift_rows(char* state);
AES_API static void inv_sub_bytes(char* state);
AES_API static char inv_affine(char value);
AES_API static void write_to_row(unsigned int value, char* to, size_t row);
AES_API static unsigned int rotate(unsigned int i);
AES_API static unsigned int get_row(char* from, size_t row);
AES_API static char rot(char value, size_t by);
};
}}

View File

@ -69,13 +69,17 @@
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup> </ImportGroup>
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>$(SolutionDir)XMath;$(IncludePath)</IncludePath>
<LibraryPath>$(SolutionDir)Debug;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)XMath;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -115,6 +119,15 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="AES.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\XMath\XMath.vcxproj">
<Project>{5f9ad03f-b1f0-4db3-b39a-49b66895774c}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="AES.cpp" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View File

@ -14,4 +14,14 @@
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ClInclude Include="AES.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="AES.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project> </Project>

View File

@ -77,7 +77,7 @@
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>C:\Users\Lenovo\source\repos\Crypto\XMath;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(SolutionDir)AES;$(SolutionDir)XMath;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -120,6 +120,9 @@
<ClCompile Include="Start.cpp" /> <ClCompile Include="Start.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\AES\AES.vcxproj">
<Project>{e1176bbf-344b-4041-ab24-a7109e969339}</Project>
</ProjectReference>
<ProjectReference Include="..\XMath\XMath.vcxproj"> <ProjectReference Include="..\XMath\XMath.vcxproj">
<Project>{5f9ad03f-b1f0-4db3-b39a-49b66895774c}</Project> <Project>{5f9ad03f-b1f0-4db3-b39a-49b66895774c}</Project>
</ProjectReference> </ProjectReference>

View File

@ -2,6 +2,7 @@
#include "BigInteger.h" #include "BigInteger.h"
#include "Matrix.h" #include "Matrix.h"
#include "Galois.h" #include "Galois.h"
#include "AES.h"
using namespace CryptoCPP::Math; using namespace CryptoCPP::Math;

View File

@ -248,6 +248,20 @@ namespace CryptoCPP{
return new Galois(characteristic, (BLOCK*)do_copy(irreducible, irreducible_size * sizeof(BLOCK)), irreducible_size, result, result_size); return new Galois(characteristic, (BLOCK*)do_copy(irreducible, irreducible_size * sizeof(BLOCK)), irreducible_size, result, result_size);
} }
GALOIS_API BLOCK * Galois::to_array(size_t * size)
{
BLOCK * b = new BLOCK[data_size];
memcpy(b, data, data_size);
if(size!=0) *size = data_size;
return b;
}
GALOIS_API BLOCK Galois::get_lowest()
{
return data_size == 0 ? 0 : data[0];
}
// These internal functions assume that an adequate state size has been chose // These internal functions assume that an adequate state size has been chose
GALOIS_API void Galois::iadd(BLOCK * data, size_t data_size, size_t bin_size, BLOCK * state, size_t state_size, BLOCK characteristic) GALOIS_API void Galois::iadd(BLOCK * data, size_t data_size, size_t bin_size, BLOCK * state, size_t state_size, BLOCK characteristic)
{ {

View File

@ -53,6 +53,10 @@ namespace CryptoCPP {
// Inverse multiplication // Inverse multiplication
GALOIS_API Galois * inv() const; GALOIS_API Galois * inv() const;
// Get internal value
GALOIS_API BLOCK * to_array(size_t * size = 0);
GALOIS_API BLOCK get_lowest();
protected: protected:
static const BLOCK high_bit = 1 << ((sizeof(BLOCK) * 8) - 1); static const BLOCK high_bit = 1 << ((sizeof(BLOCK) * 8) - 1);
// GF parameters & value // GF parameters & value