/*---------------------------------------------------------------------------* Project: Horizon File: FileTransfer.cpp Copyright 2009 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev$ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Aes_define.h" #include "FileTransfer.h" #include "CommonLogger.h" #include "demo.h" #include #include #include "DrawSystemState.h" #include "FileName.h" #include "SimplePlayer.h" #include "CommonLogger.h" #include "SDMountManager.h" #include "HeapManager.h" #include "common_Types.h" #include "VersionDetect.h" #include "Util.h" #include "CommonLogger.h" namespace { // グラフィックスに割り当てるメモリ const size_t s_GxHeapSize = 0x800000; const u32 CONSOLE_WIDTH = 38; const u32 CONSOLE_HEIGHT = 24; const u32 CONSOLE_MAX_LINE = 1000; const size_t VERIFY_THREAD_STACK_SIZE = 0x4000; nn::os::Thread s_EncryptThread; nn::os::StackBuffer s_EncryptThreadStack; u32 s_EncryptSuccess = 0; u32 s_EncryptFail = 0; } void GenerateNandPath(wchar_t* toPath, const wchar_t* fromPath) { // 切り詰める std::string tmp(common::GetCharStr(fromPath)); std::string twlRoot(common::GetCharStr(common::SD_SAVEDATA_TWL_ROOT_NAME)); std::string twlPhotoRoot(common::GetCharStr(common::SD_SAVEDATA_TWL_PHOTO_ROOT_NAME)); std::string twlSoundRoot(common::GetCharStr(common::SD_SAVEDATA_TWL_SOUND_ROOT_NAME)); std::string ctrRoot(common::GetCharStr(common::SD_SAVEDATA_ROOT_NAME)); std::string output; std::string::size_type size; size = tmp.find(twlPhotoRoot.c_str()); if(size == std::string::npos) { size = tmp.find(twlSoundRoot.c_str()); if(size == std::string::npos) { size = tmp.find(ctrRoot.c_str()); if(size == std::string::npos) { size = tmp.find(twlRoot.c_str()); if(size == std::string::npos) { // 想定外のパスへの出力のためreturn return; } else { output += std::string("twln:/title/"); output += tmp.substr(size + twlRoot.size()); } } else { output += std::string("nand:/data/"); output += tmp.substr(size + ctrRoot.size()); } } else { output += std::string("twls:/"); output += tmp.substr(size + twlSoundRoot.size()); } } else { output += std::string("twlp:/"); output += tmp.substr(size + twlPhotoRoot.size()); } s32 length = std::mbstowcs(toPath, output.c_str(), nn::fs::MAX_FILE_PATH_LENGTH); if(length == -1) { NN_PANIC("failed mbstowcs"); } } // ディレクトリ間のコピー // アーカイブ越しのコピーが可能 // アーカイブにマウントした状態で呼び出す必要あり // 書き込み先のディレクトリはあらかじめ消去しておくこと。 // 引数はスラッシュ付き // TODO:分割して短くする bool EncryptDirectory(const wchar_t * from_path, void* buf, const size_t bufSize) { using namespace common; nn::fs::Directory from_dir; nn::fs::DirectoryEntry entry; s32 numread = 0; std::wostringstream target_from; bool ret_value = true; nn::Result result = from_dir.TryInitialize(from_path); COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result); while (1) { result = from_dir.TryRead(&numread, &entry, 1); if (result.IsFailure() || numread != 1) { break; } if (std::wcscmp(entry.entryName, L".") == 0 || std::wcscmp(entry.entryName, L"..") == 0) { continue; } target_from.str(L""); target_from.clear(std::stringstream::goodbit); target_from << from_path << entry.entryName; // ディレクトリの場合 if (entry.attributes.isDirectory) { { target_from << L"/"; // 再帰処理 if (!EncryptDirectory(target_from.str().c_str(), buf, bufSize)) { ret_value = false; } } } // ファイルの場合 // SDカード上のファイルを暗号化してCMACを出力する else { nn::fs::FileInputStream sdFile; s64 filesize; s32 readsize; // 読み込み対象ファイル開く result = sdFile.TryInitialize(target_from.str().c_str()); COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result); // 読み込み対象ファイルのサイズ取得 result = sdFile.TryGetSize(&filesize); COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result); nn::crypto::SwAesCtrContext swAesCtrContext; swAesCtrContext.Initialize(iv, common::key, sizeof(key)); size_t totalReadSize = 0; nn::crypto::Sha256Context context; context.Initialize(); // ファイルサイズをヘッダに書いておく { wchar_t nandPath[nn::fs::MAX_FILE_PATH_LENGTH]; // sdパスからnandパスを生成する GenerateNandPath(nandPath, target_from.str().c_str()); // NAND上のフルパスをハッシュに含めている context.Update(nandPath, std::wcslen(nandPath) * sizeof(wchar_t)); BackupDataHeader header; BackupDataHeader enc; std::memset(&header, 0, sizeof(header)); std::memset(&enc, 0, sizeof(enc)); header.size = filesize; result = swAesCtrContext.Encrypt(&enc, &header, sizeof(header)); COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result); context.Update(&enc, sizeof(enc)); } while (1) { // バッファの後半半分を暗号・復号用に使う result = sdFile.TryRead(&readsize, buf, bufSize / 2); COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result); totalReadSize += readsize; if (readsize == 0) { { NN_LOG("Add CMAC %ls\n", target_from.str().c_str()); // SHA256を計算してCMACを付加する bit8 sha256Hash[nn::crypto::Sha256Context::HASH_SIZE]; context.GetHash(sha256Hash); bit8 cmac[nn::crypto::AES_CMAC_MAC_SIZE]; result = nn::crypto::CalculateAesCmacSw(cmac, sha256Hash, nn::crypto::Sha256Context::HASH_SIZE, common::cmacKey); COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result); NN_LOG("cmac:\n"); for(u32 i = 0; i < sizeof(cmac); i++) { NN_LOG("%02X ", cmac[i]); } NN_LOG("\n"); } break; } else { { NN_LOG("EncryptSize = %d\n", readsize); u8 paddingSize = 0; common::AddPkcsPadding(&paddingSize, reinterpret_cast(buf), bufSize / 2, &readsize); // 暗号化後SHA256を計算しつつ書き込み result = swAesCtrContext.Encrypt(reinterpret_cast(buf) + bufSize / 2, buf, readsize); COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result); context.Update(reinterpret_cast(buf) + bufSize / 2, readsize); // 事前計算したファイルサイズに一致させるためパディング分減算 readsize -= paddingSize; } COMMON_LOGGER_RETURN_FALSE_IF_FAILED(result); } } sdFile.Finalize(); } } from_dir.Finalize(); return ret_value; } void EncryptThreadFunc() { nn::Result result; COMMON_LOGGER("EncryptThreadFunc Start\n"); s_EncryptFail = 0; s_EncryptSuccess = 0; result = common::SdMountManager::Mount(); size_t bufSize = common::GetAllocatableSize(AES_BLOCK_SIZE * 2); if(bufSize > common::FILE_COPY_HEAP_SIZE) { bufSize = common::FILE_COPY_HEAP_SIZE; } common::HeapManager heap(bufSize, AES_BLOCK_SIZE * 2); void* buf = heap.GetAddr(); if (buf != NULL) { EncryptDirectory( (std::wstring(common::SDMC_ROOT_DIRECTORY_PATH) + ::std::wstring(common::SD_SAVEDATA_TWL_ROOT_NAME)).c_str(), buf, bufSize); EncryptDirectory( (std::wstring(common::SDMC_ROOT_DIRECTORY_PATH) + ::std::wstring(common::SD_SAVEDATA_TWL_SOUND_ROOT_NAME)).c_str(), buf, bufSize); EncryptDirectory( (std::wstring(common::SDMC_ROOT_DIRECTORY_PATH) + ::std::wstring(common::SD_SAVEDATA_TWL_PHOTO_ROOT_NAME)).c_str(), buf, bufSize); EncryptDirectory( (std::wstring(common::SDMC_ROOT_DIRECTORY_PATH) + ::std::wstring(common::SD_SAVEDATA_ROOT_NAME)).c_str(), buf, bufSize); } common::SdMountManager::Unmount(); COMMON_LOGGER("Encrypt Thread Finalize\n"); COMMON_LOGGER("\n\n"); COMMON_LOGGER("Encrypt Finished, success = %d, fail = %d\n", s_EncryptSuccess, s_EncryptFail); } extern "C" void nninitSetupDaemons(void) { } extern "C" void nnMain(void) { nn::Result result; // os の初期化 nn::os::Initialize(); // fs の初期化 nn::fs::Initialize(); // appletの初期化 nn::applet::Enable( false ); // hid の初期化 result = nn::hid::Initialize(); NN_ERR_THROW_FATAL_IF_FATAL_ONLY(result); // ヒープの確保 common::InitializeHeap(); // RenderSystem の準備 common::HeapManager gxHeap(s_GxHeapSize); uptr heapForGx = reinterpret_cast(gxHeap.GetAddr()); demo::RenderSystemDrawing s_RenderSystem; s_RenderSystem.Initialize(heapForGx, s_GxHeapSize); // ログ描画の初期化 common::Logger::GetLoggerInstance()->Initialize(CONSOLE_WIDTH, CONSOLE_HEIGHT, CONSOLE_MAX_LINE, &s_RenderSystem); // RenderSystemを作ってからログが出せる common::Logger::InitializeEjectThread(); COMMON_LOGGER("Encrypt Start\n"); // ボタン入力 nn::hid::PadReader s_PadReader; nn::hid::PadStatus padStatus; for(;;) { s_PadReader.ReadLatest(&padStatus); if(padStatus.trigger & nn::hid::BUTTON_A) { // SDにコピーするためのスレッドの作成 if(s_EncryptThread.IsValid() && !s_EncryptThread.IsAlive()) { s_EncryptThread.Join(); s_EncryptThread.Finalize(); } s_EncryptThread.Start(EncryptThreadFunc, s_EncryptThreadStack); } // コンソールスクロール if(padStatus.hold & nn::hid::BUTTON_UP) { common::Logger::GetLoggerInstance()->ScrollUp(); } // コンソールスクロール if(padStatus.hold & nn::hid::BUTTON_DOWN) { common::Logger::GetLoggerInstance()->ScrollDown(); } if(padStatus.hold & nn::hid::BUTTON_LEFT) { common::Logger::GetLoggerInstance()->ScrollToBegin(); } if(padStatus.hold & nn::hid::BUTTON_RIGHT) { common::Logger::GetLoggerInstance()->ScrollToEnd(); } s_RenderSystem.SetRenderTarget(NN_GX_DISPLAY0); s_RenderSystem.Clear(); s_RenderSystem.SetColor(1.f, 1.f, 1.f); common::Logger::GetLoggerInstance()->DrawConsole(); s_RenderSystem.SwapBuffers(); s_RenderSystem.WaitVsync(NN_GX_DISPLAY_BOTH); } }