From 7561db5ed2f1c92de28580b0ab5bff097264055a Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 17 May 2020 02:36:48 -0700 Subject: [PATCH] exo2: Implement (untested) SmcDecryptDeviceUniqueData --- .../crypto_aes_impl_security_engine.cpp | 89 +++++ libvapours/include/vapours/crypto.hpp | 1 + .../crypto/crypto_aes_gcm_encryptor.hpp | 73 +++++ .../vapours/crypto/crypto_gcm_encryptor.hpp | 64 ++++ .../vapours/crypto/impl/crypto_aes_impl.hpp | 8 + .../crypto/impl/crypto_gcm_mode_impl.hpp | 106 ++++++ .../include/vapours/util/util_endian.hpp | 15 +- .../impl/crypto_aes_impl.arch.arm64.cpp | 2 + .../impl/crypto_gcm_mode_impl.arch.arm64.cpp | 305 ++++++++++++++++++ 9 files changed, 655 insertions(+), 8 deletions(-) create mode 100644 libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp create mode 100644 libvapours/include/vapours/crypto/crypto_aes_gcm_encryptor.hpp create mode 100644 libvapours/include/vapours/crypto/crypto_gcm_encryptor.hpp create mode 100644 libvapours/include/vapours/crypto/impl/crypto_gcm_mode_impl.hpp create mode 100644 libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp diff --git a/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp b/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp new file mode 100644 index 00000000..d71f7244 --- /dev/null +++ b/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#include + +namespace ams::crypto::impl { + + namespace { + + constexpr bool IsSupportedKeySize(size_t size) { + return size == 16 || size == 24 || size == 32; + } + + } + + template + AesImpl::~AesImpl() { + ClearMemory(this, sizeof(*this)); + } + + template + void AesImpl::Initialize(const void *key, size_t key_size, bool is_encrypt) { + static_assert(IsSupportedKeySize(KeySize)); + + /* Set the security engine keyslot. */ + this->slot = *static_cast(key); + } + + template + void AesImpl::EncryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const { + static_assert(IsSupportedKeySize(KeySize)); + AMS_ASSERT(src_size >= BlockSize); + AMS_ASSERT(dst_size >= BlockSize); + + if constexpr (KeySize == 16) { + /* Aes 128. */ + se::EncryptAes128(dst, dst_size, this->slot, src, src_size); + } else if constexpr (KeySize == 24) { + /* Aes 192. */ + /* TODO: se::EncryptAes192(dst, dst_size, this->slot, src, src_size); */ + } else if constexpr (KeySize == 32) { + /* Aes 256. */ + /* TODO: se::EncryptAes256(dst, dst_size, this->slot, src, src_size); */ + } else { + /* Invalid key size. */ + static_assert(!std::is_same, AesImpl>::value); + } + } + + template + void AesImpl::DecryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const { + static_assert(IsSupportedKeySize(KeySize)); + AMS_ASSERT(src_size >= BlockSize); + AMS_ASSERT(dst_size >= BlockSize); + + if constexpr (KeySize == 16) { + /* Aes 128. */ + se::DecryptAes128(dst, dst_size, this->slot, src, src_size); + } else if constexpr (KeySize == 24) { + /* Aes 192. */ + /* TODO: se::DecryptAes192(dst, dst_size, this->slot, src, src_size); */ + } else if constexpr (KeySize == 32) { + /* Aes 256. */ + /* TODO: se::DecryptAes256(dst, dst_size, this->slot, src, src_size); */ + } else { + /* Invalid key size. */ + static_assert(!std::is_same, AesImpl>::value); + } + } + + + /* Explicitly instantiate the three supported key sizes. */ + template class AesImpl<16>; + template class AesImpl<24>; + template class AesImpl<32>; + +} diff --git a/libvapours/include/vapours/crypto.hpp b/libvapours/include/vapours/crypto.hpp index 2aa531f6..0126dc99 100644 --- a/libvapours/include/vapours/crypto.hpp +++ b/libvapours/include/vapours/crypto.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/libvapours/include/vapours/crypto/crypto_aes_gcm_encryptor.hpp b/libvapours/include/vapours/crypto/crypto_aes_gcm_encryptor.hpp new file mode 100644 index 00000000..326d738b --- /dev/null +++ b/libvapours/include/vapours/crypto/crypto_aes_gcm_encryptor.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018-2020 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 { + + namespace impl { + + template + class AesGcmEncryptor { + NON_COPYABLE(AesGcmEncryptor); + NON_MOVEABLE(AesGcmEncryptor); + private: + using AesImpl = _AesImpl; + using GcmImpl = GcmEncryptor; + public: + static constexpr size_t KeySize = AesImpl::KeySize; + static constexpr size_t BlockSize = AesImpl::BlockSize; + static constexpr size_t MacSize = AesImpl::BlockSize; + private: + AesImpl aes_impl; + GcmImpl gcm_impl; + public: + AesGcmEncryptor() { /* ... */ } + + void Initialize(const void *key, size_t key_size, const void *iv, size_t iv_size) { + this->aes_impl.Initialize(key, key_size); + this->gcm_impl.Initialize(std::addressof(this->aes_impl), iv, iv_size); + } + + void Reset(const void *iv, size_t iv_size) { + this->gcm_impl.Reset(iv, iv_size); + } + + size_t Update(void *dst, size_t dst_size, const void *src, size_t src_size) { + return this->gcm_impl.Update(dst, dst_size, src, src_size); + } + + void UpdateAad(const void *aad, size_t aad_size) { + return this->gcm_impl.UpdateAad(aad, aad_size); + } + + void GetMac(void *dst, size_t dst_size) { + return this->gcm_impl.GetMac(dst, dst_size); + } + }; + + } + + using Aes128GcmEncryptor = impl::AesGcmEncryptor; + /* TODO: Validate AAD/GMAC is same for non-128 bit key using Aes192GcmEncryptor = impl::AesGcmEncryptor; */ + /* TODO: Validate AAD/GMAC is same for non-128 bit key using Aes256GcmEncryptor = impl::AesGcmEncryptor; */ + +} diff --git a/libvapours/include/vapours/crypto/crypto_gcm_encryptor.hpp b/libvapours/include/vapours/crypto/crypto_gcm_encryptor.hpp new file mode 100644 index 00000000..547bd739 --- /dev/null +++ b/libvapours/include/vapours/crypto/crypto_gcm_encryptor.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2020 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 { + + /* TODO: C++20 BlockCipher concept */ + + template + class GcmEncryptor { + NON_COPYABLE(GcmEncryptor); + NON_MOVEABLE(GcmEncryptor); + private: + using Impl = impl::GcmModeImpl; + public: + static constexpr size_t KeySize = Impl::KeySize; + static constexpr size_t BlockSize = Impl::BlockSize; + static constexpr size_t MacSize = Impl::MacSize; + private: + Impl impl; + public: + GcmEncryptor() { /* ... */ } + + void Initialize(const BlockCipher *cipher, const void *iv, size_t iv_size) { + this->impl.Initialize(cipher); + this->impl.Reset(iv, iv_size); + } + + void Reset(const void *iv, size_t iv_size) { + this->impl.Reset(iv, iv_size); + } + + size_t Update(void *dst, size_t dst_size, const void *src, size_t src_size) { + return this->impl.Update(dst, dst_size, src, src_size); + } + + void UpdateAad(const void *aad, size_t aad_size) { + return this->impl.UpdateAad(aad, aad_size); + } + + void GetMac(void *dst, size_t dst_size) { + return this->impl.GetMac(dst, dst_size); + } + }; + +} diff --git a/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp b/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp index d99b1477..51391721 100644 --- a/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp +++ b/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp @@ -30,16 +30,24 @@ namespace ams::crypto::impl { static constexpr s32 RoundCount = (KeySize / 4) + 6; static constexpr size_t RoundKeySize = BlockSize * (RoundCount + 1); private: + #ifdef ATMOSPHERE_IS_EXOSPHERE + int slot; + #endif + #ifdef ATMOSPHERE_IS_STRATOSPHERE u32 round_keys[RoundKeySize / sizeof(u32)]; + #endif public: ~AesImpl(); void Initialize(const void *key, size_t key_size, bool is_encrypt); void EncryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const; void DecryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const; + + #ifdef ATMOSPHERE_IS_STRATOSPHERE const u8 *GetRoundKey() const { return reinterpret_cast(this->round_keys); } + #endif }; /* static_assert(HashFunction); */ diff --git a/libvapours/include/vapours/crypto/impl/crypto_gcm_mode_impl.hpp b/libvapours/include/vapours/crypto/impl/crypto_gcm_mode_impl.hpp new file mode 100644 index 00000000..fbe974f2 --- /dev/null +++ b/libvapours/include/vapours/crypto/impl/crypto_gcm_mode_impl.hpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018-2020 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::impl { + + template + class GcmModeImpl { + NON_COPYABLE(GcmModeImpl); + NON_MOVEABLE(GcmModeImpl); + public: + static constexpr size_t KeySize = BlockCipher::KeySize; + static constexpr size_t BlockSize = BlockCipher::BlockSize; + static constexpr size_t MacSize = BlockCipher::BlockSize; + private: + enum State { + State_None, + State_Initialized, + State_ProcessingAad, + State_Encrypting, + State_Decrypting, + State_Done, + }; + + struct Block128 { + u64 hi; + u64 lo; + + ALWAYS_INLINE void Clear() { + this->hi = 0; + this->lo = 0; + } + }; + static_assert(util::is_pod::value); + static_assert(sizeof(Block128) == 0x10); + + union Block { + Block128 block_128; + u32 block_32[4]; + u8 block_8[16]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(Block) == 0x10); + + using CipherFunction = void (*)(void *dst_block, const void *src_block, const void *ctx); + private: + State state; + const BlockCipher *block_cipher; + CipherFunction cipher_func; + u8 pad[sizeof(u64)]; + Block block_x; + Block block_y; + Block block_ek; + Block block_ek0; + Block block_tmp; + size_t aad_size; + size_t msg_size; + u32 aad_remaining; + u32 msg_remaining; + u32 counter; + Block h_mult_blocks[16]; + public: + GcmModeImpl() : state(State_None) { /* ... */ } + + ~GcmModeImpl() { + ClearMemory(this, sizeof(*this)); + } + + void Initialize(const BlockCipher *block_cipher); + + void Reset(const void *iv, size_t iv_size); + + void UpdateAad(const void *aad, size_t aad_size); + size_t UpdateEncrypt(void *dst, size_t dst_size, const void *src, size_t src_size); + size_t UpdateDecrypt(void *dst, size_t dst_size, const void *src, size_t src_size); + + void GetMac(void *dst, size_t dst_size); + private: + static void ProcessBlock(void *dst_block, const void *src_block, const void *ctx) { + static_cast(ctx)->EncryptBlock(dst_block, BlockSize, src_block, BlockSize); + } + + void InitializeHashKey(); + void ComputeMac(bool encrypt); + }; + +} diff --git a/libvapours/include/vapours/util/util_endian.hpp b/libvapours/include/vapours/util/util_endian.hpp index f269796d..147a73f8 100644 --- a/libvapours/include/vapours/util/util_endian.hpp +++ b/libvapours/include/vapours/util/util_endian.hpp @@ -43,7 +43,6 @@ namespace ams::util { ((u & (ByteMask << 16)) << 24) | ((u & (ByteMask << 8)) << 40) | ((u & (ByteMask << 0)) << 56); - } else if constexpr (std::is_same::value) { return ((u & (ByteMask << 24)) >> 24) | ((u & (ByteMask << 16)) >> 8) | @@ -79,7 +78,7 @@ namespace ams::util { constexpr ALWAYS_INLINE void SwapBytes(T *ptr) { using U = typename std::make_unsigned::type; - *ptr = static_cast(SwapBytes(static_cast(*ptr))); + *ptr = static_cast(SwapBytes(static_cast(*ptr))); } template requires std::integral @@ -90,7 +89,7 @@ namespace ams::util { return static_cast(static_cast(val)); } else { static_assert(IsLittleEndian()); - return static_cast(SwapBytes(static_cast(val))); + return static_cast(SwapBytes(static_cast(val))); } } @@ -99,7 +98,7 @@ namespace ams::util { using U = typename std::make_unsigned::type; if constexpr (IsBigEndian()) { - return static_cast(SwapBytes(static_cast(val))); + return static_cast(SwapBytes(static_cast(val))); } else { static_assert(IsLittleEndian()); return static_cast(static_cast(val)); @@ -136,22 +135,22 @@ namespace ams::util { template requires std::integral constexpr ALWAYS_INLINE T LoadBigEndian(const T *ptr) { - return ConvertToBigEndian(*ptr); + return ConvertToBigEndian(*ptr); } template requires std::integral constexpr ALWAYS_INLINE T LoadLittleEndian(const T *ptr) { - return ConvertToLittleEndian(*ptr); + return ConvertToLittleEndian(*ptr); } template requires std::integral constexpr ALWAYS_INLINE void StoreBigEndian(T *ptr, T val) { - *ptr = ConvertToBigEndian(val); + *ptr = ConvertToBigEndian(val); } template requires std::integral constexpr ALWAYS_INLINE void StoreLittleEndian(T *ptr, T val) { - *ptr = ConvertToLittleEndian(val); + *ptr = ConvertToLittleEndian(val); } } diff --git a/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp b/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp index 8c7465be..e1ad2c81 100644 --- a/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp +++ b/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp @@ -111,6 +111,8 @@ namespace ams::crypto::impl { #else + /* NOTE: Exosphere defines this in libexosphere. */ + /* TODO: Non-EL0 implementation. */ #endif diff --git a/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp b/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp new file mode 100644 index 00000000..1b7c2995 --- /dev/null +++ b/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#include + +#if defined(ATMOSPHERE_IS_STRATOSPHERE) + +/* TODO: EL0 implementation. */ +namespace ams::crypto::impl { + +} + +#else + +/* EL1+ implementation. */ +namespace ams::crypto::impl { + + namespace { + + constexpr u64 GetMultiplyFactor(u8 value) { + constexpr size_t Shift = BITSIZEOF(u8) - 1; + constexpr u8 Mask = (1u << Shift); + return (value & Mask) >> Shift; + } + + /* TODO: Big endian support, eventually? */ + constexpr void GaloisShiftLeft(u64 *block) { + /* Shift the block left by one. */ + block[1] <<= 1; + block[1] |= (block[0] & (static_cast(1) << (BITSIZEOF(u64) - 1))) >> (BITSIZEOF(u64) - 1); + block[0] <<= 1; + } + + constexpr u8 GaloisShiftRight(u64 *block) { + /* Determine the mask to return. */ + constexpr u8 GaloisFieldMask = 0xE1; + const u8 mask = (block[0] & 1) * GaloisFieldMask; + + /* Shift the block right by one. */ + block[0] >>= 1; + block[0] |= (block[1] & 1) << (BITSIZEOF(u64) - 1); + block[1] >>= 1; + + /* Return the mask. */ + return mask; + } + + /* Multiply two 128-bit numbers X, Y in the GF(128) Galois Field. */ + void GaloisFieldMult(void *dst, const void *x, const void *y) { + /* Our block size is 16 bytes (for a 128-bit integer). */ + constexpr size_t BlockSize = 16; + constexpr size_t FieldSize = 128; + + /* Declare work blocks for us to store temporary values. */ + u8 x_block[BlockSize]; + u8 y_block[BlockSize]; + u8 out[BlockSize]; + + /* Declare 64-bit pointers for our convenience. */ + u64 *x_64 = static_cast(static_cast(x_block)); + u64 *y_64 = static_cast(static_cast(y_block)); + u64 *out_64 = static_cast(static_cast(out)); + + /* Initialize our work blocks. */ + for (size_t i = 0; i < BlockSize; ++i) { + x_block[i] = static_cast(x)[BlockSize - 1 - i]; + y_block[i] = static_cast(y)[BlockSize - 1 - i]; + out[i] = 0; + } + + /* Perform multiplication on each bit in y. */ + for (size_t i = 0; i < FieldSize; ++i) { + /* Get the multiply factor for this bit. */ + const auto y_mult = GetMultiplyFactor(y_block[BlockSize - 1]); + + /* Multiply x by the factor. */ + out_64[0] ^= x_64[0] * y_mult; + out_64[1] ^= x_64[1] * y_mult; + + /* Shift left y by one. */ + GaloisShiftLeft(y_64); + + /* Shift right x by one, and mask appropriately. */ + const u8 x_mask = GaloisShiftRight(x_64); + x_block[BlockSize - 1] ^= x_mask; + } + + /* Copy out our result. */ + for (size_t i = 0; i < BlockSize; ++i) { + static_cast(dst)[i] = out[BlockSize - 1 - i]; + } + } + + } + + template + void GcmModeImpl::Initialize(const BlockCipher *block_cipher) { + /* Set member variables. */ + this->block_cipher = block_cipher; + this->cipher_func = std::addressof(GcmModeImpl::ProcessBlock); + + /* Pre-calculate values to speed up galois field multiplications later. */ + this->InitializeHashKey(); + + /* Note that we're initialized. */ + this->state = State_Initialized; + } + + template + void GcmModeImpl::Reset(const void *iv, size_t iv_size) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->state >= State_Initialized); + + /* Reset blocks. */ + this->block_x.block_128.Clear(); + this->block_tmp.block_128.Clear(); + + /* Clear sizes. */ + this->aad_size = 0; + this->msg_size = 0; + this->aad_remaining = 0; + this->msg_remaining = 0; + + /* Update our state. */ + this->state = State_ProcessingAad; + + /* Set our iv. */ + if (iv_size == 12) { + /* If our iv is the correct size, simply copy in the iv, and set the magic bit. */ + std::memcpy(std::addressof(this->block_ek0), iv, iv_size); + util::StoreBigEndian(this->block_ek0.block_32 + 3, static_cast(1)); + } else { + /* Clear our ek0 block. */ + this->block_ek0.block_128.Clear(); + + /* Update using the iv as aad. */ + this->UpdateAad(iv, iv_size); + + /* Treat the iv as fake msg for the mac that will become our iv. */ + this->msg_size = this->aad_size; + this->aad_size = 0; + + /* Compute a non-final mac. */ + this->ComputeMac(false); + + /* Set our ek0 block to our calculated mac block. */ + this->block_ek0 = this->block_x; + + /* Clear our calculated mac block. */ + this->block_x.block_128.Clear(); + + /* Reset our state. */ + this->msg_size = 0; + this->aad_size = 0; + this->msg_remaining = 0; + this->aad_remaining = 0; + } + + /* Set the working block to the iv. */ + this->block_ek = this->block_ek0; + } + + template + void GcmModeImpl::UpdateAad(const void *aad, size_t aad_size) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->state == State_ProcessingAad); + AMS_ASSERT(this->msg_size == 0); + + /* Update our aad size. */ + this->aad_size += aad_size; + + /* Define a working tracker variable. */ + const u8 *cur_aad = static_cast(aad); + + /* Process any leftover aad data from a previous invocation. */ + if (this->aad_remaining > 0) { + while (aad_size > 0) { + /* Copy in a byte of the aad to our partial block. */ + this->block_x.block_8[BlockSize - 1 - this->aad_remaining] ^= *(cur_aad++); + + /* Note that we consumed a byte. */ + --aad_size; + + /* Increment our partial block size. */ + this->aad_remaining = (this->aad_remaining + 1) % BlockSize; + + /* If we have a complete block, process it and move onward. */ + GaloisFieldMult(std::addressof(this->block_x), std::addressof(this->block_x), std::addressof(this->h_mult_blocks[0])); + } + } + + /* Process as many blocks as we can. */ + while (aad_size >= BlockSize) { + /* Xor the current aad into our work block. */ + for (size_t i = 0; i < BlockSize; ++i) { + this->block_x.block_8[BlockSize - 1 - i] ^= *(cur_aad++); + } + + /* Multiply the blocks in our galois field. */ + GaloisFieldMult(std::addressof(this->block_x), std::addressof(this->block_x), std::addressof(this->h_mult_blocks[0])); + + /* Note that we've processed a block. */ + aad_size -= BlockSize; + } + + /* Update our state with whatever aad is left over. */ + if (aad_size > 0) { + /* Note how much left over data we have. */ + this->aad_remaining = static_cast(aad_size); + + /* Xor the data in. */ + for (size_t i = 0; i < aad_size; ++i) { + this->block_x.block_8[BlockSize - 1 - i] ^= *(cur_aad++); + } + } + } + + /* TODO: template size_t GcmModeImpl::UpdateEncrypt(void *dst, size_t dst_size, const void *src, size_t src_size); */ + + /* TODO: template size_t GcmModeImpl::UpdateDecrypt(void *dst, size_t dst_size, const void *src, size_t src_size); */ + + template + void GcmModeImpl::GetMac(void *dst, size_t dst_size) { + /* Validate pre-conditions. */ + AMS_ASSERT(State_ProcessingAad <= this->state && this->state <= State_Done); + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(dst_size >= MacSize); + AMS_ASSERT(this->aad_remaining == 0); + AMS_ASSERT(this->msg_remaining == 0); + + /* If we haven't already done so, compute the final mac. */ + if (this->state != State_Done) { + this->ComputeMac(true); + this->state = State_Done; + } + + static_assert(sizeof(this->block_x) == MacSize); + std::memcpy(dst, std::addressof(this->block_x), MacSize); + } + + template + void GcmModeImpl::InitializeHashKey() { + /* We want to encrypt an empty block to use for intermediate calculations. */ + /* NOTE: Non-EL1 implementations will do multiple encryptions ahead of time, */ + /* to speed up galois field arithmetic. */ + constexpr const Block EmptyBlock = {}; + + this->ProcessBlock(std::addressof(this->h_mult_blocks[0]), std::addressof(EmptyBlock), this->block_cipher); + } + + template + void GcmModeImpl::ComputeMac(bool encrypt) { + /* If we have leftover data, process it. */ + if (this->aad_remaining > 0 || this->msg_remaining > 0) { + GaloisFieldMult(std::addressof(this->block_x), std::addressof(this->block_x), std::addressof(this->h_mult_blocks[0])); + } + + /* Setup the last block. */ + Block last_block = Block{ .block_128 = { this->msg_size, this->aad_size } }; + + /* Multiply the last block by 8 to account for bit vs byte sizes. */ + static_assert(offsetof(Block128, hi) == 0); + GaloisShiftLeft(std::addressof(last_block.block_128.hi)); + GaloisShiftLeft(std::addressof(last_block.block_128.hi)); + GaloisShiftLeft(std::addressof(last_block.block_128.hi)); + + /* Xor the data in. */ + for (size_t i = 0; i < BlockSize; ++i) { + this->block_x.block_8[BlockSize - 1 - i] ^= last_block.block_8[i]; + } + + /* Perform the final multiplication. */ + GaloisFieldMult(std::addressof(this->block_x), std::addressof(this->block_x), std::addressof(this->h_mult_blocks[0])); + + /* If we need to do an encryption, do so. */ + { + /* Encrypt the iv. */ + u8 enc_result[BlockSize]; + this->ProcessBlock(enc_result, std::addressof(this->block_ek0), this->block_cipher); + + /* Xor the iv in. */ + for (size_t i = 0; i < BlockSize; ++i) { + this->block_x.block_8[i] ^= enc_result[i]; + } + } + } + + /* Explicitly instantiate the valid template classes. */ + template class GcmModeImpl; + +} + +#endif