diff --git a/libraries/libvapours/include/vapours/crypto.hpp b/libraries/libvapours/include/vapours/crypto.hpp index 63cfcb2e4..205bbb7e6 100644 --- a/libraries/libvapours/include/vapours/crypto.hpp +++ b/libraries/libvapours/include/vapours/crypto.hpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/crypto/crypto_aes_128_cmac_generator.hpp b/libraries/libvapours/include/vapours/crypto/crypto_aes_128_cmac_generator.hpp new file mode 100644 index 000000000..c252263e1 --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/crypto_aes_128_cmac_generator.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace ams::crypto { + + class Aes128CmacGenerator { + NON_COPYABLE(Aes128CmacGenerator); + NON_MOVEABLE(Aes128CmacGenerator); + public: + static constexpr size_t MacSize = AesEncryptor128::BlockSize; + private: + AesEncryptor128 m_aes; + CmacGenerator m_cmac_generator; + public: + Aes128CmacGenerator() { /* ... */ } + + void Initialize(const void *key, size_t key_size) { + AMS_ASSERT(key_size == AesEncryptor128::KeySize); + + m_aes.Initialize(key, key_size); + m_cmac_generator.Initialize(std::addressof(m_aes)); + } + + void Update(const void *data, size_t size) { + m_cmac_generator.Update(data, size); + } + + void GetMac(void *dst, size_t size) { + m_cmac_generator.GetMac(dst, size); + } + }; + + ALWAYS_INLINE void GenerateAes128Cmac(void *dst, size_t dst_size, const void *data, size_t data_size, const void *key, size_t key_size) { + Aes128CmacGenerator cmac_generator; + + cmac_generator.Initialize(key, key_size); + cmac_generator.Update(data, data_size); + cmac_generator.GetMac(dst, dst_size); + } + +} diff --git a/libraries/libvapours/include/vapours/crypto/crypto_cmac_generator.hpp b/libraries/libvapours/include/vapours/crypto/crypto_cmac_generator.hpp new file mode 100644 index 000000000..052dab42e --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/crypto_cmac_generator.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include + +namespace ams::crypto { + + template + class CmacGenerator { + NON_COPYABLE(CmacGenerator); + NON_MOVEABLE(CmacGenerator); + private: + using Impl = impl::CmacImpl; + public: + static constexpr size_t MacSize = BlockCipher::BlockSize; + private: + Impl m_impl; + public: + CmacGenerator() { /* ... */ } + + void Initialize(const BlockCipher *cipher) { + return m_impl.Initialize(cipher); + } + + void Update(const void *data, size_t size) { + return m_impl.Update(data, size); + } + + void GetMac(void *dst, size_t dst_size) { + return m_impl.GetMac(dst, dst_size); + } + }; + +} diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_cmac_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_cmac_impl.hpp new file mode 100644 index 000000000..fb237540d --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_cmac_impl.hpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace ams::crypto::impl { + + template + class CmacImpl { + NON_COPYABLE(CmacImpl); + NON_MOVEABLE(CmacImpl); + public: + static constexpr size_t BlockSize = BlockCipher::BlockSize; + static constexpr size_t MacSize = BlockSize; + static_assert(BlockSize == 0x10); /* TODO: Should this be supported? */ + private: + enum State { + State_None = 0, + State_Initialized = 1, + State_Done = 2, + }; + private: + CbcMacImpl m_cbc_mac_impl; + u8 m_sub_key[BlockSize]; + State m_state; + public: + CmacImpl() : m_state(State_None) { /* ... */ } + ~CmacImpl() { + /* Clear everything. */ + ClearMemory(this, sizeof(*this)); + } + + void Initialize(const BlockCipher *cipher); + void Update(const void *data, size_t data_size); + void GetMac(void *dst, size_t dst_size); + private: + static void MultiplyOneOverGF128(u8 *data) { + /* Determine the carry bit. */ + const u8 carry = data[0] & 0x80; + + /* Shift all bytes by one bit. */ + for (size_t i = 0; i < BlockSize - 1; ++i) { + data[i] = (data[i] << 1) | (data[i + 1] >> 7); + } + data[BlockSize - 1] <<= 1; + + /* Adjust based on carry. */ + if (carry) { + data[BlockSize - 1] ^= 0x87; + } + } + }; + + template + inline void CmacImpl::Initialize(const BlockCipher *cipher) { + /* Clear the key storage. */ + std::memset(m_sub_key, 0, sizeof(m_sub_key)); + + /* Set the key storage. */ + cipher->EncryptBlock(m_sub_key, BlockSize, m_sub_key, BlockSize); + MultiplyOneOverGF128(m_sub_key); + + /* Initialize the cbc-mac impl. */ + m_cbc_mac_impl.Initialize(cipher); + + /* Mark initialized. */ + m_state = State_Initialized; + } + + template + inline void CmacImpl::Update(const void *data, size_t data_size) { + AMS_ASSERT(m_state == State_Initialized); + + m_cbc_mac_impl.template Update(data, data_size); + } + + template + inline void CmacImpl::GetMac(void *dst, size_t dst_size) { + AMS_ASSERT(m_state == State_Initialized || m_state == State_Done); + AMS_ASSERT(dst_size >= MacSize); + AMS_UNUSED(dst_size); + + /* If we're not already finalized, get the final mac. */ + if (m_state == State_Initialized) { + /* Process padding as needed. */ + if (m_cbc_mac_impl.GetBufferedDataSize() != BlockSize) { + /* Determine the remaining size. */ + const size_t remaining = BlockSize - m_cbc_mac_impl.GetBufferedDataSize(); + + /* Update with padding. */ + static constexpr u8 s_padding[BlockSize] = { 0x80, /* ... */ }; + m_cbc_mac_impl.template Update(s_padding, remaining); + + /* Update our subkey. */ + MultiplyOneOverGF128(m_sub_key); + } + + /* Mask the subkey. */ + m_cbc_mac_impl.MaskBufferedData(m_sub_key, BlockSize); + + /* Set our state as done. */ + m_state = State_Done; + } + + /* Get the mac. */ + m_cbc_mac_impl.GetMac(dst, dst_size); + } + +}