Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for aes 128 ecb and cmac #796

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions services/util/Aes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,32 @@ namespace services
virtual void SetKey(const std::array<uint8_t, 16>& key) = 0;
virtual void Encrypt(std::array<uint8_t, 16>& counter, infra::ConstByteRange input, infra::ByteRange output) = 0;
};

class Aes128Ecb
{
public:
Aes128Ecb() = default;
Aes128Ecb(const Aes128Ecb& other) = delete;
Aes128Ecb& operator=(const Aes128Ecb& other) = delete;

virtual void SetKey(const std::array<uint8_t, 16>& key) const = 0;
virtual void Encrypt(infra::ConstByteRange input, infra::ByteRange output) const = 0;
virtual void Decrypt(infra::ConstByteRange input, infra::ByteRange output) const = 0;
};

class Aes128Cmac
{
public:
Aes128Cmac() = default;
Aes128Cmac(const Aes128Cmac& other) = delete;
Aes128Cmac& operator=(const Aes128Cmac& other) = delete;

using Mac = std::array<uint8_t, 16>;

virtual void SetKey(const std::array<uint8_t, 16>& key) = 0;
virtual void Append(infra::ConstByteRange input) = 0;
virtual Mac Calculate() = 0;
};
}

#endif
136 changes: 136 additions & 0 deletions services/util/AesCmac.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#include "services/util/AesCmac.hpp"
#include <algorithm>

namespace
{
uint32_t Reverse(uint32_t data)
{
return ((data & 0x000000FF) << 24) |
((data & 0x0000FF00) << 8) |
((data & 0x00FF0000) >> 8) |
((data & 0xFF000000) >> 24);
}

void KeyRoll(infra::ByteRange data)
{
auto key = infra::ReinterpretCastMemoryRange<uint32_t>(data);

auto carry = ((key.front() >> 31) & 1) * 0x87;

for (std::size_t i = 1; i != key.size(); i++)
key[i - 1] = (key[i - 1] << 1) | (key[i] >> 31);

key.back() = (key.back() << 1) ^ carry;
}

infra::ConstByteRange Xor(infra::ConstByteRange x, infra::ByteRange y)
{
auto data = infra::ConstCastMemoryRange<uint32_t>(infra::ReinterpretCastMemoryRange<const uint32_t>(x));
auto key = infra::ReinterpretCastMemoryRange<uint32_t>(y);

for (std::size_t i = 0; i != data.size(); i++)
data[i] = Reverse(data[i]) ^ (key[i]);

return infra::ReinterpretCastByteRange(data);
}

infra::ConstByteRange XorIncludingResult(infra::ByteRange x, infra::ByteRange y, infra::ByteRange result)
{
auto res = infra::ReinterpretCastMemoryRange<uint32_t>(result);
auto tag = infra::ReinterpretCastMemoryRange<uint32_t>(x);
auto key = infra::ReinterpretCastMemoryRange<uint32_t>(y);

for (std::size_t i = 0; i < res.size(); i++)
res[i] ^= tag[i] ^ key[i];

return infra::ReinterpretCastByteRange(res);
}

void AddByteBigEndianFormat(infra::ByteRange input, std::size_t position, uint32_t data)
{
auto inputRange = infra::ReinterpretCastMemoryRange<uint32_t>(input);

inputRange[position / 4] |= data << (8 * (3 - (position % 4)));
}

std::size_t CalculateRemainingBytes(std::size_t size, std::size_t blockSize)
{
auto remainingBytes = size % blockSize;
if ((size != 0) && (remainingBytes == 0))
remainingBytes = 16;

return remainingBytes;
}

void CopyReversedWords(infra::ByteRange destination, infra::ByteRange source)
{
auto dest = infra::ReinterpretCastMemoryRange<uint32_t>(destination);
auto src = infra::ReinterpretCastMemoryRange<uint32_t>(source);

for (std::size_t i = 0; i != dest.size(); ++i)
dest[i] = Reverse(src[i]);
}
}

namespace services
{
Aes128CmacImpl::Aes128CmacImpl(Aes128Ecb& aes128Ecb)
: aes128Ecb(aes128Ecb)
{}

void Aes128CmacImpl::SetKey(const std::array<uint8_t, 16>& key)
{
lastBlockSize = 0;
tag.fill(0);
aes128Ecb.SetKey(key);
}

void Aes128CmacImpl::Append(infra::ConstByteRange input)
{
std::array<uint8_t, blockSize> chunk{};
lastBlockSize = CalculateRemainingBytes(input.size(), blockSize);
std::copy(input.end() - lastBlockSize, input.end(), lastBlock.begin());

input = infra::DiscardTail(input, lastBlockSize);

while (!input.empty())
{
std::copy(input.begin(), input.begin() + blockSize, chunk.begin());
aes128Ecb.Encrypt(Xor(chunk, tag), tag);
input = infra::DiscardHead(input, blockSize);
}
}

Aes128Cmac::Mac Aes128CmacImpl::Calculate()
{
std::array<uint8_t, blockSize> subkey{};
std::array<uint8_t, blockSize> temp{};

for (std::size_t i = 0; i != lastBlockSize; ++i)
AddByteBigEndianFormat(temp, i, lastBlock[i]);

ComputeK1(subkey);

if (lastBlockSize < blockSize)
ComputeK2(temp, subkey);

aes128Ecb.Encrypt(XorIncludingResult(tag, subkey, temp), tag);

CopyReversedWords(temp, tag);
tag = temp;

return tag;
}

void Aes128CmacImpl::ComputeK1(infra::ByteRange key)
{
aes128Ecb.Encrypt(key, key);
KeyRoll(key);
}

void Aes128CmacImpl::ComputeK2(infra::ByteRange block, infra::ByteRange key)
{
AddByteBigEndianFormat(block, lastBlockSize, 0x80);
KeyRoll(key);
}
}
31 changes: 31 additions & 0 deletions services/util/AesCmac.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef SERVICES_AES_CMAC_HPP
#define SERVICES_AES_CMAC_HPP

#include "services/util/Aes.hpp"

namespace services
{
class Aes128CmacImpl
: public Aes128Cmac
{
public:
explicit Aes128CmacImpl(Aes128Ecb& aes128Ecb);

void SetKey(const std::array<uint8_t, 16>& key) override;
void Append(infra::ConstByteRange input) override;
Mac Calculate() override;

private:
void ComputeK1(infra::ByteRange key);
void ComputeK2(infra::ByteRange block, infra::ByteRange key);

private:
const static std::size_t blockSize = 16;
Aes128Ecb& aes128Ecb;
std::array<uint8_t, blockSize> tag;
std::array<uint8_t, blockSize> lastBlock;
std::size_t lastBlockSize = 0;
};
}

#endif
2 changes: 2 additions & 0 deletions services/util/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ target_link_libraries(services.util PUBLIC

target_sources(services.util PRIVATE
Aes.hpp
AesCmac.cpp
AesCmac.hpp
ConfigurationStore.cpp
ConfigurationStore.hpp
CyclicStore.cpp
Expand Down
1 change: 1 addition & 0 deletions services/util/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ target_link_libraries(services.util_test PUBLIC
)

target_sources(services.util_test PRIVATE
TestAesCmac.cpp
$<$<BOOL:${EMIL_INCLUDE_MBEDTLS}>:TestAesMbedTls.cpp>
$<$<BOOL:${EMIL_INCLUDE_MBEDTLS}>:TestConfigurationStore.cpp>
TestCyclicStore.cpp
Expand Down
153 changes: 153 additions & 0 deletions services/util/test/TestAesCmac.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#include "infra/util/test_helper/MemoryRangeMatcher.hpp"
#include "services/util/AesCmac.hpp"
#include <gmock/gmock.h>

namespace
{
template<class T, std::size_t N>
void Copy(std::array<T, N> from, infra::MemoryRange<T> to)
{
assert(from.size() == to.size());
std::copy(from.begin(), from.begin() + from.size(), to.begin());
}

class Aes128EcbMock
: public services::Aes128Ecb
{
public:
using Key = std::array<uint8_t, 16>;

MOCK_METHOD(void, SetKey, (const Key&), (const, override));
MOCK_METHOD(void, Encrypt, (infra::ConstByteRange, infra::ByteRange), (const, override));
MOCK_METHOD(void, Decrypt, (infra::ConstByteRange, infra::ByteRange), (const, override));
};

class Aes128CmacImplTest
: public testing::Test

{
public:
Aes128CmacImplTest()
{
EXPECT_CALL(aes128EcbMock, SetKey(key));
aes.SetKey(key);
}

testing::StrictMock<Aes128EcbMock> aes128EcbMock;
services::Aes128CmacImpl aes{ aes128EcbMock };
std::array<uint8_t, 16> key{ { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c } };
};
}

TEST_F(Aes128CmacImplTest, plain_data_empty)
{
std::array<uint8_t, 16> macExpected{ { 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 } };

::testing::InSequence _;

EXPECT_CALL(aes128EcbMock, Encrypt(infra::ContentsEqual(std::array{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }), ::testing::_)).WillOnce([](auto, auto result)
{
Copy(std::array<uint8_t, 16>{ 0x0c, 0x6b, 0xf7, 0x7d, 0xb3, 0x99, 0xb8, 0x1a, 0x47, 0xf0, 0x42, 0x3e, 0x6f, 0x54, 0x1b, 0xb9 }, result);
});

EXPECT_CALL(aes128EcbMock, Encrypt(infra::ContentsEqual(std::array{ 0x30, 0xac, 0xdd, 0x77, 0xcc, 0x66, 0xe2, 0x6a, 0x1e, 0xc1, 0x0b, 0xf9, 0x3b, 0x51, 0x6d, 0xe4 }), ::testing::_)).WillOnce([](auto, auto result)
{
Copy(std::array<uint8_t, 16>{ 0x29, 0x69, 0x1d, 0xbb, 0x28, 0x37, 0x59, 0xe9, 0x12, 0x7d, 0xa3, 0x7f, 0x46, 0x67, 0x75, 0x9b }, result);
});

aes.Append(infra::ConstByteRange());
EXPECT_TRUE(aes.Calculate() == macExpected);
}

TEST_F(Aes128CmacImplTest, plain_data_16_bytes)
{
std::array<uint8_t, 16> plainData = { { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a } };
std::array<uint8_t, 16> macExpected{ { 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c } };

::testing::InSequence _;

EXPECT_CALL(aes128EcbMock, Encrypt(infra::ContentsEqual(std::array{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }), ::testing::_)).WillOnce([](auto, auto result)
{
Copy(std::array<uint8_t, 16>{ 0x0c, 0x6b, 0xf7, 0x7d, 0xb3, 0x99, 0xb8, 0x1a, 0x47, 0xf0, 0x42, 0x3e, 0x6f, 0x54, 0x1b, 0xb9 }, result);
});

EXPECT_CALL(aes128EcbMock, Encrypt(infra::ContentsEqual(std::array{ 0xfa, 0x68, 0x2f, 0x90, 0xf0, 0xac, 0x31, 0x1b, 0x9e, 0x9e, 0xb8, 0x95, 0xf4, 0xbf, 0xa5, 0x01 }), ::testing::_)).WillOnce([](auto, auto result)
{
Copy(std::array<uint8_t, 16>{ 0xb4, 0x16, 0x0a, 0x07, 0x44, 0x41, 0x4d, 0x6b, 0x9d, 0xdd, 0x9b, 0xf7, 0x7c, 0x28, 0x4a, 0xd0 }, result);
});

aes.Append(plainData);
EXPECT_TRUE(aes.Calculate() == macExpected);
}

TEST_F(Aes128CmacImplTest, plain_data_40_bytes)
{
std::array<uint8_t, 40> plainData = { { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 } };
std::array<uint8_t, 16> macExpected{ { 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 } };

::testing::InSequence _;

EXPECT_CALL(aes128EcbMock, Encrypt(infra::ContentsEqual(std::array{ 0xe2, 0xbe, 0xc1, 0x6b, 0x96, 0x9f, 0x40, 0x2e, 0x11, 0x7e, 0x3d, 0xe9, 0x2a, 0x17, 0x93, 0x73 }), ::testing::_)).WillOnce([](auto, auto result)
{
Copy(std::array<uint8_t, 16>{ 0xb4, 0x7b, 0xd7, 0x3a, 0x60, 0x36, 0x7a, 0x0d, 0xf3, 0xca, 0x9e, 0xa8, 0x97, 0xef, 0x66, 0x24 }, result);
});

EXPECT_CALL(aes128EcbMock, Encrypt(infra::ContentsEqual(std::array{ 0xe3, 0xf1, 0xfa, 0x94, 0xfc, 0x9a, 0x79, 0x13, 0x5f, 0xa5, 0x29, 0x36, 0xc6, 0x61, 0xc9, 0x61 }), ::testing::_)).WillOnce([](auto, auto result)
{
Copy(std::array<uint8_t, 16>{ 0x7f, 0xc1, 0x48, 0xb1, 0x92, 0xe6, 0x9e, 0x30, 0x7c, 0xe5, 0x7a, 0x28, 0x49, 0xdd, 0x2a, 0xf1 }, result);
});

EXPECT_CALL(aes128EcbMock, Encrypt(infra::ContentsEqual(std::array{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }), ::testing::_)).WillOnce([](auto, auto result)
{
Copy(std::array<uint8_t, 16>{ 0x0c, 0x6b, 0xf7, 0x7d, 0xb3, 0x99, 0xb8, 0x1a, 0x47, 0xf0, 0x42, 0x3e, 0x6f, 0x54, 0x1b, 0xb9 }, result);
});

EXPECT_CALL(aes128EcbMock, Encrypt(infra::ContentsEqual(std::array{ 0x09, 0x71, 0x5d, 0x76, 0x4f, 0x64, 0x20, 0xf9, 0x62, 0x24, 0x71, 0x51, 0x72, 0x8c, 0x47, 0x15 }), ::testing::_)).WillOnce([](auto, auto result)
{
Copy(std::array<uint8_t, 16>{ 0x47, 0x67, 0xa6, 0xdf, 0x30, 0xe6, 0x9a, 0xde, 0x61, 0x32, 0xca, 0x30, 0x27, 0xc8, 0x97, 0x14 }, result);
});

aes.Append(plainData);
EXPECT_TRUE(aes.Calculate() == macExpected);
}

TEST_F(Aes128CmacImplTest, plain_data_64_bytes)
{
std::array<uint8_t, 64> plainData = { { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 } };
std::array<uint8_t, 16> macExpected{ { 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe } };

::testing::InSequence _;

EXPECT_CALL(aes128EcbMock, Encrypt(infra::ContentsEqual(std::array{ 0xe2, 0xbe, 0xc1, 0x6b, 0x96, 0x9f, 0x40, 0x2e, 0x11, 0x7e, 0x3d, 0xe9, 0x2a, 0x17, 0x93, 0x73 }), ::testing::_)).WillOnce([](auto, auto result)
{
Copy(std::array<uint8_t, 16>{ 0xb4, 0x7b, 0xd7, 0x3a, 0x60, 0x36, 0x7a, 0x0d, 0xf3, 0xca, 0x9e, 0xa8, 0x97, 0xef, 0x66, 0x24 }, result);
});

EXPECT_CALL(aes128EcbMock, Encrypt(infra::ContentsEqual(std::array{ 0xe3, 0xf1, 0xfa, 0x94, 0xfc, 0x9a, 0x79, 0x13, 0x5f, 0xa5, 0x29, 0x36, 0xc6, 0x61, 0xc9, 0x61 }), ::testing::_)).WillOnce([](auto, auto result)
{
Copy(std::array<uint8_t, 16>{ 0x7f, 0xc1, 0x48, 0xb1, 0x92, 0xe6, 0x9e, 0x30, 0x7c, 0xe5, 0x7a, 0x28, 0x49, 0xdd, 0x2a, 0xf1 }, result);
});

EXPECT_CALL(aes128EcbMock, Encrypt(infra::ContentsEqual(std::array{ 0x39, 0xdd, 0x80, 0x81, 0x83, 0x02, 0xc2, 0x93, 0x65, 0x24, 0x81, 0xcd, 0xa6, 0x8f, 0x20, 0xeb }), ::testing::_)).WillOnce([](auto, auto result)
{
Copy(std::array<uint8_t, 16>{ 0xbf, 0x11, 0x3d, 0xc9, 0xdc, 0xc5, 0x08, 0xaf, 0x7b, 0xb3, 0x90, 0x4d, 0x2b, 0x00, 0xee, 0x4d }, result);
});

EXPECT_CALL(aes128EcbMock, Encrypt(infra::ContentsEqual(std::array{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }), ::testing::_)).WillOnce([](auto, auto result)
{
Copy(std::array<uint8_t, 16>{ 0x0c, 0x6b, 0xf7, 0x7d, 0xb3, 0x99, 0xb8, 0x1a, 0x47, 0xf0, 0x42, 0x3e, 0x6f, 0x54, 0x1b, 0xb9 }, result);
});

EXPECT_CALL(aes128EcbMock, Encrypt(infra::ContentsEqual(std::array{ 0xe2, 0xe3, 0x4c, 0xc4, 0xad, 0x6d, 0x36, 0x45, 0x8f, 0x12, 0x3e, 0x9c, 0xe5, 0x9f, 0xb4, 0xd9 }), ::testing::_)).WillOnce([](auto, auto result)
{
Copy(std::array<uint8_t, 16>{ 0xbf, 0xbe, 0xf0, 0x51, 0x92, 0x9d, 0x3b, 0x7e, 0x17, 0x74, 0x49, 0xfc, 0xfe, 0x3c, 0x36, 0x79 }, result);
});

aes.Append(plainData);
EXPECT_TRUE(aes.Calculate() == macExpected);
}
Loading