mirror of
				https://github.com/Atmosphere-NX/Atmosphere-libs.git
				synced 2025-10-31 11:36:02 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			131 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			131 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * 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 <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| #include <stratosphere.hpp>
 | |
| 
 | |
| namespace ams::fssystem {
 | |
| 
 | |
|     AesCtrStorageExternal::AesCtrStorageExternal(std::shared_ptr<fs::IStorage> bs, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, DecryptAesCtrFunction df, s32 kidx, s32 kgen) : m_base_storage(std::move(bs)), m_decrypt_function(df), m_key_index(kidx), m_key_generation(kgen) {
 | |
|         AMS_ASSERT(m_base_storage != nullptr);
 | |
|         AMS_ASSERT(enc_key_size == KeySize);
 | |
|         AMS_ASSERT(iv != nullptr);
 | |
|         AMS_ASSERT(iv_size == IvSize);
 | |
|         AMS_UNUSED(iv_size);
 | |
| 
 | |
|         std::memcpy(m_iv, iv, IvSize);
 | |
|         std::memcpy(m_encrypted_key, enc_key, enc_key_size);
 | |
|     }
 | |
| 
 | |
|     Result AesCtrStorageExternal::Read(s64 offset, void *buffer, size_t size) {
 | |
|         /* Allow zero size. */
 | |
|         R_SUCCEED_IF(size == 0);
 | |
| 
 | |
|         /* Validate arguments. */
 | |
|         /* NOTE: For some reason, Nintendo uses InvalidArgument instead of InvalidOffset/InvalidSize here. */
 | |
|         R_UNLESS(buffer != nullptr,                  fs::ResultNullptrArgument());
 | |
|         R_UNLESS(util::IsAligned(offset, BlockSize), fs::ResultInvalidArgument());
 | |
|         R_UNLESS(util::IsAligned(size,   BlockSize), fs::ResultInvalidArgument());
 | |
| 
 | |
|         /* Read the data. */
 | |
|         R_TRY(m_base_storage->Read(offset, buffer, size));
 | |
| 
 | |
|         /* Temporarily increase our thread priority. */
 | |
|         ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative);
 | |
| 
 | |
|         /* Allocate a pooled buffer for decryption. */
 | |
|         PooledBuffer pooled_buffer;
 | |
|         pooled_buffer.AllocateParticularlyLarge(size, BlockSize);
 | |
|         AMS_ASSERT(pooled_buffer.GetSize() >= BlockSize);
 | |
| 
 | |
|         /* Setup the counter. */
 | |
|         u8 ctr[IvSize];
 | |
|         std::memcpy(ctr, m_iv, IvSize);
 | |
|         AddCounter(ctr, IvSize, offset / BlockSize);
 | |
| 
 | |
|         /* Setup tracking. */
 | |
|         size_t remaining_size = size;
 | |
|         s64 cur_offset = 0;
 | |
| 
 | |
|         while (remaining_size > 0) {
 | |
|             /* Get the current size to process. */
 | |
|             size_t cur_size = std::min(pooled_buffer.GetSize(), remaining_size);
 | |
|             char *dst = static_cast<char *>(buffer) + cur_offset;
 | |
| 
 | |
|             /* Decrypt into the temporary buffer */
 | |
|             m_decrypt_function(pooled_buffer.GetBuffer(), cur_size, m_key_index, m_key_generation, m_encrypted_key, KeySize, ctr, IvSize, dst, cur_size);
 | |
| 
 | |
|             /* Copy to the destination. */
 | |
|             std::memcpy(dst, pooled_buffer.GetBuffer(), cur_size);
 | |
| 
 | |
|             /* Update tracking. */
 | |
|             cur_offset     += cur_size;
 | |
|             remaining_size -= cur_size;
 | |
| 
 | |
|             if (remaining_size > 0) {
 | |
|                 AddCounter(ctr, IvSize, cur_size / BlockSize);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         R_SUCCEED();
 | |
|     }
 | |
| 
 | |
|     Result AesCtrStorageExternal::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
 | |
|         switch (op_id) {
 | |
|             case fs::OperationId::QueryRange:
 | |
|                 {
 | |
|                     /* Validate that we have an output range info. */
 | |
|                     R_UNLESS(dst != nullptr,                         fs::ResultNullptrArgument());
 | |
|                     R_UNLESS(dst_size == sizeof(fs::QueryRangeInfo), fs::ResultInvalidSize());
 | |
| 
 | |
|                     /* Operate on our base storage. */
 | |
|                     R_TRY(m_base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
 | |
| 
 | |
|                     /* Add in new flags. */
 | |
|                     fs::QueryRangeInfo new_info;
 | |
|                     new_info.Clear();
 | |
|                     new_info.aes_ctr_key_type = static_cast<s32>(m_key_index >= 0 ? fs::AesCtrKeyTypeFlag::InternalKeyForHardwareAes : fs::AesCtrKeyTypeFlag::ExternalKeyForHardwareAes);
 | |
| 
 | |
|                     /* Merge the new info in. */
 | |
|                     reinterpret_cast<fs::QueryRangeInfo *>(dst)->Merge(new_info);
 | |
|                     R_SUCCEED();
 | |
|                 }
 | |
|             default:
 | |
|                 {
 | |
|                     /* Operate on our base storage. */
 | |
|                     R_RETURN(m_base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
 | |
|                 }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     Result AesCtrStorageExternal::GetSize(s64 *out) {
 | |
|         R_RETURN(m_base_storage->GetSize(out));
 | |
|     }
 | |
| 
 | |
|     Result AesCtrStorageExternal::Flush() {
 | |
|         R_SUCCEED();
 | |
|     }
 | |
| 
 | |
|     Result AesCtrStorageExternal::Write(s64 offset, const void *buffer, size_t size) {
 | |
|         AMS_UNUSED(offset, buffer, size);
 | |
|         R_THROW(fs::ResultUnsupportedWriteForAesCtrStorageExternal());
 | |
|     }
 | |
| 
 | |
|     Result AesCtrStorageExternal::SetSize(s64 size) {
 | |
|         AMS_UNUSED(size);
 | |
|         R_THROW(fs::ResultUnsupportedSetSizeForAesCtrStorageExternal());
 | |
|     }
 | |
| 
 | |
| }
 |